Kritim Yantra
Aug 12, 2025
Alright — let’s keep the momentum going.
We’ve done:
Now for Part 3: Adding Authentication to Express.js + GraphQL — because what good is an API if anyone can poke around in 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.
We’ll use jsonwebtoken
for creating tokens and bcryptjs
for hashing passwords.
npm install bcryptjs jsonwebtoken
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);
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
}
`);
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);
}
};
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 }
};
}));
mutation {
register(name: "Alice", email: "alice@example.com", password: "mypassword")
}
You’ll get a token.
mutation {
login(email: "alice@example.com", password: "mypassword")
}
{
me {
id
name
email
}
}
Remember to add the token in headers:
{
"Authorization": "Bearer YOUR_TOKEN"
}
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.
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.
You’ve now added real authentication to your Express.js + GraphQL API. That means:
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google