Role-Based Access Control in Express.js + GraphQL: Who Gets to Do What?
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.
The "Oops" That Happens Without Roles…
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.
Why RBAC Matters
- Protects your data — Only trusted users can perform sensitive actions.
- Keeps users happy — Prevents accidental (or intentional) damage.
- Scales well — Easier to manage permissions for thousands of users.
Step 1: Add a Role to Your User Model
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
});
Step 2: Update the GraphQL Schema
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
}
`);
Step 3: Create an authorize Helper
Instead 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');
}
}
Step 4: Secure Resolvers with RBAC
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' });
}
};
Step 5: Testing Role-Based Access
- Register an Admin
mutation {
register(name: "Admin", email: "admin@example.com", password: "adminpass", role: "ADMIN")
}
- Register a Regular User
mutation {
register(name: "Bob", email: "bob@example.com", password: "bobpass")
}
- Try
allUsersQuery with Bob's Token → ❌ Access Denied - Try
allUsersQuery with Admin's Token → ✅ Success
Pro Tip 💡
Think of RBAC like a VIP party:
- USER: Regular guest — can enjoy the drinks.
- ADMIN: Party host — can kick people out, change the music, and open the VIP lounge.
Common Beginner Questions (FAQ)
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.
Wrapping Up
You’ve now:
- Added roles to your GraphQL users
- Secured certain actions for admins only
- Built a cleaner, reusable
authorizehelper
Your API is now safer and ready for real-world multi-user scenarios.
Projects to explore next
Source-code projects from the KritiMyantra catalogue. Check each project's details before buying.
Flask Authentication System (Free Version) - Kritim Yantra
🛠 Features✔ Google & GitHub Authentication✔ User Registration & Login✔ Profile/Dashboard Access✔ Secure & Mi...
Flask Blog CMS with Admin Dashboard — Python Source Code
A Flask-based blogging website with a built-in admin dashboard. Manage articles, images, categories, tags and featured p...
Laravel 12 + ReactJS Starter Kit: Add Multi-Language Support (Arabic, English, Spanish) with Blog CRUD
🚀 Installation & Setup Download & Extract Get the ZIP from kritimyantra.com and extract it. Includes preconfigu...
Comments
No comments yet. Be the first to comment.
Sign in to join the discussion.