I'm always excited to take on new projects and collaborate with innovative minds.

Social Links

Web Development

Building a Role-Based Access System in Node.js Without a Framework

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.

Building a Role-Based Access System in Node.js Without a Framework

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.


Why Go Framework-Free?

Frameworks are great, but sometimes you need something more minimal:

  • You're building a microservice with minimal overhead.
  • You want to deeply understand RBAC.
  • You need full control over request handling.
  • You’re building a custom runtime, CLI tool, or embedded service.

In such cases, writing your own RBAC layer gives you flexibility, portability, and insight.


Understanding Role-Based Access Control (RBAC)

RBAC works on a simple concept:

  1. Users have one or more roles.
  2. Roles have one or more permissions.
  3. Access to a resource or action is granted based on the permissions of the user's role(s).

Let’s structure our system around these three pieces.


Step 1: Define Users, Roles, and Permissions

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.


Step 2: Create the Access Control Function

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.


Step 3: Integrate With a Custom HTTP Server

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=alice
  • http://localhost:3000/edit?user=bob
  • http://localhost:3000/admin?user=eve

Step 4: Improve Maintainability (Optional)

As 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.


Lessons Learned

  • RBAC doesn’t require complex tooling.
  • Designing your access logic early keeps your codebase clean.
  • Building it yourself gives you more flexibility and less overhead.
  • Node.js, even without Express, is capable of handling access control securely and efficiently.

GitHub Repository

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!


Final Thoughts

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:

  • Replacing in-memory data with a DB or caching layer.
  • Supporting role inheritance or composite permissions.
  • Using JWT tokens with embedded roles/claims.
  • Adding middleware-style access hooks for modularity.

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.

node.js, rbac, access control, role-based access, node.js security, node without express, authorization, backend, system design, no framework, pure node, user roles, permission system, access rights, minimal node.js app
4 min read
Jul 20, 2025
By Dheer Gupta
Share

Leave a comment

Your email address will not be published. Required fields are marked *

Related posts

Jun 28, 2025 • 4 min read
Implementing Rate Limiting on Web APIs using ASP.NET Core and Redis
Jun 27, 2025 • 4 min read
Building Custom ASP.NET Core Middleware for Request-Response Logging with Elasticsearch (Centralized API Monitoring)