I'm always excited to take on new projects and collaborate with innovative minds.
Learn how to build a role-based access control (RBAC) system in pure Node.js without using any frameworks. This guide walks you through users, roles, permissions, and access validation using native modules only.
When people think of role-based access control (RBAC) in Node.js, they usually jump to frameworks like Express or NestJS. But what if you're building something lightweight, framework-free, or just want to understand what's going on under the hood?
In this post, we’ll walk through how to build a clean, scalable role-based access system in pure Node.js — no frameworks, no dependencies — just raw JavaScript, structured thinking, and smart design.
Frameworks are great, but sometimes you need something more minimal:
In such cases, writing your own RBAC layer gives you flexibility, portability, and insight.
RBAC works on a simple concept:
Let’s structure our system around these three pieces.
Let’s keep things simple by defining everything in-memory for now:
// data.js
const roles = {
admin: ['read:any', 'write:any', 'delete:any'],
editor: ['read:any', 'write:own'],
viewer: ['read:any'],
};
const users = {
alice: { role: 'admin' },
bob: { role: 'editor' },
eve: { role: 'viewer' },
};
module.exports = { roles, users };
Here, permissions follow the format <action>:<scope>, e.g. write:own, which keeps things extensible and easy to match.
Now let’s write the logic to check if a user can perform a specific action.
// accessControl.js
const { roles, users } = require('./data');
function canPerform(username, permission) {
const user = users[username];
if (!user) return false;
const userRole = user.role;
const permissions = roles[userRole] || [];
return permissions.includes(permission);
}
This is the core of our RBAC system. It’s simple, readable, and works as a standalone utility.
Now let’s build a minimal HTTP server using Node’s http module and secure different routes based on role.
// server.js
const http = require('http');
const url = require('url');
const { canPerform } = require('./accessControl');
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const { pathname, query } = parsedUrl;
const username = query.user;
if (!username) {
res.writeHead(401);
res.end('User not provided');
return;
}
if (pathname === '/view') {
if (canPerform(username, 'read:any')) {
res.writeHead(200);
res.end(`Content visible to ${username}`);
} else {
res.writeHead(403);
res.end('Forbidden');
}
} else if (pathname === '/edit') {
if (canPerform(username, 'write:own')) {
res.writeHead(200);
res.end(`Editing allowed for ${username}`);
} else {
res.writeHead(403);
res.end('Forbidden');
}
} else if (pathname === '/admin') {
if (canPerform(username, 'delete:any')) {
res.writeHead(200);
res.end(`Admin access granted to ${username}`);
} else {
res.writeHead(403);
res.end('Forbidden');
}
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
Try it out by visiting:
http://localhost:3000/view?user=alicehttp://localhost:3000/edit?user=bobhttp://localhost:3000/admin?user=eveAs your app grows, you’ll want to extract the access rules into a dedicated service and possibly integrate async storage (e.g., a database or Redis). Here’s a sample interface you could evolve into:
class AccessService {
constructor(roles) {
this.roles = roles;
}
can(role, permission) {
return this.roles[role]?.includes(permission) ?? false;
}
userCan(user, permission) {
return this.can(user.role, permission);
}
}
This abstraction helps if you later store roles and permissions in a database or want to support role hierarchies or dynamic rules.
You can find the complete source code for this RBAC system here:
👉 GitHub Repo – RBAC in Node.js Without a Framework
Feel free to fork it, experiment, or contribute improvements!
Frameworks are helpful, but knowing how to build fundamental features like RBAC from scratch is empowering. You now have a foundation to build and scale a Node.js access control system tailored to your app’s needs.
If you plan to scale this, consider:
Whether you’re hacking on a CLI, building an internal API, or creating a lightweight backend, sometimes less is more — and this no-framework RBAC setup proves that.
Your email address will not be published. Required fields are marked *