Kritim Yantra
Aug 12, 2025
We’ve done:
Now it’s time for Role-Based Access Control (RBAC) in Express.js + GraphQL — so you can give different permissions to different users.
Imagine you’re running a website where people can write blog posts.
One day, a regular user logs in and… somehow deletes every post on the site.
Not fun. 😬
This happens when all authenticated users are treated the same.
But in real-world apps, not everyone should have the same power — admins should be able to manage everything, while regular users can only do certain things.
That’s where Role-Based Access Control (RBAC) comes in.
We’ll give each user a role
field."USER"
is the default, "ADMIN"
has all the powers.
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, unique: true },
password: String,
role: { type: String, default: 'USER' } // USER or ADMIN
});
We’ll add a query that only admins can use.
const schema = buildSchema(`
type User {
id: ID
name: String
email: String
role: String
}
type Query {
me: User
allUsers: [User] # Admin only
}
type Mutation {
register(name: String!, email: String!, password: String!, role: String): String
login(email: String!, password: String!): String
}
`);
authorize
HelperInstead of sprinkling if (user.role !== 'ADMIN')
everywhere, we’ll make a reusable function.
function authorize(user, roles = []) {
if (!user) throw new Error('Not authenticated');
if (roles.length && !roles.includes(user.role)) {
throw new Error('Not authorized');
}
}
const root = {
me: async (args, context) => {
authorize(context.user);
return await User.findById(context.user.id);
},
allUsers: async (args, context) => {
authorize(context.user, ['ADMIN']);
return await User.find();
},
register: async ({ name, email, password, role }) => {
const user = new User({ name, email, password, role });
await user.save();
return jwt.sign({ id: user.id, role: user.role }, 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, role: user.role }, SECRET, { expiresIn: '1d' });
}
};
mutation {
register(name: "Admin", email: "admin@example.com", password: "adminpass", role: "ADMIN")
}
mutation {
register(name: "Bob", email: "bob@example.com", password: "bobpass")
}
allUsers
Query with Bob's Token → ❌ Access DeniedallUsers
Query with Admin's Token → ✅ SuccessThink of RBAC like a VIP party:
Q1: Can a user have multiple roles?
A: Yes! Just store roles as an array and check accordingly.
Q2: What if I want more fine-grained control?
A: You can implement permission-based access instead of role-based (e.g., "CAN_DELETE_POST"
).
Q3: How do I promote a user to admin?
A: Add an admin-only mutation that updates the role
field in the database.
You’ve now:
authorize
helperYour API is now safer and ready for real-world multi-user scenarios.
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google