Securing Your Express.js + GraphQL API with Authentication

Author

Kritim Yantra

Aug 12, 2025

Securing Your Express.js + GraphQL API with Authentication

Alright — let’s keep the momentum going.
We’ve done:

  1. Intro to Express.js + GraphQL
  2. Connecting Express.js + GraphQL to a Database

Now for Part 3: Adding Authentication to Express.js + GraphQL — because what good is an API if anyone can poke around in your data?

When You Realize Anyone Can See Your Data…

So you just finished building your shiny new GraphQL API. You show it to your friend, and they say:

“Cool… so I can just query everyone’s emails?” 😳

Yeah… that’s when it hits you. Without authentication, your API is basically an open fridge at a party — anyone can take what they want, and you might not notice until it’s too late.

Today, we’re going to lock that fridge (a.k.a. your API) using JSON Web Tokens (JWT). Trust me, once you learn this, you’ll never build an API without it again.


Why Authentication Matters (Even for Beginners)

  • Keeps your users safe 🛡️ — No random person can access sensitive info.
  • Keeps you safe — Prevents malicious spam or abuse of your API.
  • Gives you control — You decide who can do what.

Step 1: Install the Necessary Packages

We’ll use jsonwebtoken for creating tokens and bcryptjs for hashing passwords.

npm install bcryptjs jsonwebtoken

Step 2: Update Your User Model

We’ll store hashed passwords, not plain text (because plain text passwords are a one-way ticket to disaster).

const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
  name: String,
  email: { type: String, unique: true },
  password: String
});

userSchema.pre('save', async function (next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 10);
  next();
});

const User = mongoose.model('User', userSchema);

Step 3: Extend the GraphQL Schema

We’ll add register, login, and me queries.

const schema = buildSchema(`
  type User {
    id: ID
    name: String
    email: String
  }

  type Query {
    me: User
  }

  type Mutation {
    register(name: String!, email: String!, password: String!): String
    login(email: String!, password: String!): String
  }
`);

Step 4: Implement Authentication Resolvers

const jwt = require('jsonwebtoken');
const SECRET = 'your_secret_key'; // In real apps, use environment variables

const root = {
  register: async ({ name, email, password }) => {
    const user = new User({ name, email, password });
    await user.save();
    return jwt.sign({ id: user.id }, SECRET, { expiresIn: '1d' });
  },
  login: async ({ email, password }) => {
    const user = await User.findOne({ email });
    if (!user) throw new Error('User not found');
    const valid = await bcrypt.compare(password, user.password);
    if (!valid) throw new Error('Invalid password');
    return jwt.sign({ id: user.id }, SECRET, { expiresIn: '1d' });
  },
  me: async (args, context) => {
    if (!context.user) throw new Error('Not authenticated');
    return await User.findById(context.user.id);
  }
};

Step 5: Middleware to Check Tokens

Before GraphQL resolvers run, we’ll check the Authorization header.

app.use('/graphql', graphqlHTTP((req) => {
  const token = req.headers.authorization || '';
  let user = null;
  if (token) {
    try {
      user = jwt.verify(token.replace('Bearer ', ''), SECRET);
    } catch (err) {
      console.warn('Invalid token');
    }
  }
  return {
    schema,
    rootValue: root,
    graphiql: {
        headerEditorEnabled: true
    },
    context: { user }
  };
}));

Step 6: Testing Authentication

  1. Register
mutation {
  register(name: "Alice", email: "alice@example.com", password: "mypassword")
}

You’ll get a token.

  1. Login
mutation {
  login(email: "alice@example.com", password: "mypassword")
}
  1. Get Current User
{
  me {
    id
    name
    email
  }
}

Remember to add the token in headers:

{
  "Authorization": "Bearer YOUR_TOKEN"
}

Pro Tip 💡

Think of JWT tokens like wristbands at a concert — once you’ve got one, you can go anywhere you’re allowed. Lose it or fake it? Security will kick you out.


Common Beginner Questions (FAQ)

Q1: Why not just store passwords as plain text?
A: Because if your database gets hacked, you don’t want to hand over everyone’s passwords on a silver platter.

Q2: What happens if my token expires?
A: The user just needs to log in again to get a new one.

Q3: Is JWT the only way to do authentication?
A: Nope — there are sessions, OAuth, API keys, and more. JWT is just the most common for APIs.


Wrapping Up

You’ve now added real authentication to your Express.js + GraphQL API. That means:

  • Only logged-in users can access certain queries
  • Your database is safer
  • You’re one step closer to production-ready

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Sign in with Google

Related Posts