- ⏱ Lightweight
- ⚡️Fastest RBAC (check benchmarks)
- ️🍃low dependency
- Focused on operations
- Scalable
- Each role is given specific access rights for every operation
- High granularity in assigning rights
- Wildcard and regex support for operations
- Optional database adapters (MongoDB, MySQL, PostgreSQL)
- Express, NestJS and Fastify middlewares
- Roles can be updated at runtime
This project now uses Vite to generate the bundled output
Thanks to Karl Düüna (DeadAlready) and his awesome post on medium
yarn add @rbac/rbac or npm install @rbac/rbac
This library is written in TypeScript and the published package ships with its declaration files for a great developer experience.
RBAC is a curried function thats initially takes an object with configurations, then returns another function that takes an object with roles, finally returns an object that holds "can" property that is a function.
You can use it in many ways, below is one of them:
| Property | Type | Params | Default | Description |
|---|---|---|---|---|
| logger | Function | role: String operation: String result: Boolean |
defaultLogger | Function that logs operations to console |
| enableLogger | Boolean | true | Enable or disable logger |
RBAC expects an object with roles as property names.
| Property | Type | Example | Description |
|---|---|---|---|
| can | Array | ['products:*'] |
Array of strings, list of operations that user can do, it also supports glob patterns |
| when | Function, Async Function or Promise | (params , done ) => done (null , true ) |
Optional Promise that should resolve in Truthy or Falsy, an async function that returns a boolean or Promise, or a Callback function that receives params and done as properties, should return done passing errors and result |
| inherits | Array | ['user'] |
Optional Array of strings, list of roles inherited by this role |
IMPORTANT! "when" property can be a Callback function that receives params and done, an async function that returns a boolean or Promise, or a Promise that resolves in Truthy or Falsy values. Example:
import type { Roles } from '@rbac/rbac';
interface Params {
registered: boolean;
}
const roles: Roles<Params> = {
supervisor: {
can: [{ name: 'products:find', when: (params, done) => {
// done receives error as first argument and Truthy or Falsy value as second argument
done(null, params.registered);
}}]
},
admin: {
can: [{ name: 'products:*', when: async (params) => {
return params.registered;
} }]
}
};| Param | Type | Example | Description |
|---|---|---|---|
| First | String | 'admin' |
Array of strings, list of operations that user can do |
| Second | String, Glob (Wildcard), Regex | 'products:find' |
Operation to validate |
| Third | Any | {registered: true} |
Optional Params that will flow to "when" callback Function |
RBAC exposes two helpers to modify the role definition at runtime. addRole adds a new role and updateRoles merges new definitions with the existing ones.
import RBAC from '@rbac/rbac'
const base = RBAC({ enableLogger: false })({
user: { can: ['products:find'] }
})
base.addRole('editor', { can: ['products:update'], inherits: ['user'] })
await base.can('editor', 'products:update') // true
base.updateRoles({
user: { can: ['products:find', 'products:create'] }
})
await base.can('user', 'products:create') // trueRBAC exposes optional adapters to load and persist role definitions using
MongoDB, MySQL or PostgreSQL. Each adapter implements the RoleAdapter
interface with getRoles, addRole and updateRoles methods.
import RBAC from '@rbac/rbac'
import { MongoRoleAdapter } from '@rbac/rbac/adapters'
const adapter = new MongoRoleAdapter({
uri: 'mongodb://localhost:27017',
dbName: 'mydb',
collection: 'roles'
})
const roles = await adapter.getRoles()
const rbac = RBAC()(roles)Adapters available:
MongoRoleAdapterMySQLRoleAdapterPostgresRoleAdapter
Adapters also allow customizing the underlying table or collection column names
through a columns option when creating a new instance:
const adapter = new MySQLRoleAdapter({
table: 'roles',
columns: { name: 'rname', role: 'rdef', tenantId: 'tid' }
})Adapters can optionally receive a tenantId parameter to store and retrieve
roles for different tenants. When omitted, the adapter falls back to a default
tenant so existing single-tenant usage keeps working. Use createTenantRBAC to
instantiate an RBAC instance scoped to a tenant:
import { MongoRoleAdapter, createTenantRBAC } from '@rbac/rbac';
const adapter = new MongoRoleAdapter({ uri: 'mongodb://localhost:27017', dbName: 'mydb', collection: 'roles' });
await adapter.addRole('user', { can: ['products:find'] }, 'tenant-a');
const rbacTenantA = await createTenantRBAC(adapter, 'tenant-a');
await rbacTenantA.can('user', 'products:find'); // trueWant more? Check out the examples folder.
RBAC also provides helper middlewares for Express, NestJS and Fastify. They make it easy to guard routes using existing role definitions.
import RBAC, { createExpressMiddleware } from '@rbac/rbac';
const rbac = RBAC({ enableLogger: false })({
user: { can: ['products:find'] }
});
const canFindProducts = createExpressMiddleware(rbac)('products:find');
app.get('/products', canFindProducts, handler);For NestJS and Fastify you can use createNestMiddleware and createFastifyMiddleware
respectively with a similar API.
- Wildcard support
- Regex support
- Update roles in runtime
- Async
whencallbacks - Database adapters (MongoDB, MySQL, PostgreSQL)
- Middlewares for Express, NestJS and Fastify
- Rewritten in TypeScript
- Internal refactor focused on readability and performance
- Added support to update roles at runtime
- Database adapters
- Middlewares for Express, NestJS and Fastify
Run npm run bench to execute the performance suite. The script runs two end-to-end scenarios:
- Baseline comparison – compares
@rbac/rbacwith AccessControl, RBAC, Easy RBAC and Fast RBAC using the default dataset. - Large dataset comparison – stresses the libraries with hundreds of resources, deep inheritance chains and three
whenflavours (callback, async function and promise).
For each scenario the suite generates detailed reports (JSON/CSV/HTML chart) under benchmarks/results/ and prints a human-readable summary (ops/sec, margins, standard deviation, samples, etc.).
$ npm run bench
RBAC Performance Comparison ops/sec: 6859456, 6193737, 4427263, ...
RBAC Performance Comparison - Large Dataset ops/sec: 3277352, 3396327, 3424089, ...
The baseline run shows @rbac/rbac leading all categories; the large dataset confirms the same behaviour when conditional checks and large permission sets come into play.
- Build RBAC
- Run
npm install(oryarn install) to get RBAC's dependencies - Run
npm run buildto compile the library and produce the minified bundle using Vite
- Development mode
- Having all the dependencies installed run
yarn dev. This command will generate a non-minified version of your library and will run a watcher so you get the compilation on file change.
- Running the tests
- Run
yarn test
- Scripts
npm run build- produces production version of your library under thelibfolder and generateslib/@rbac/rbac.min.jsvia Vitenpm run dev- produces development version of your library and runs a watchernpm test- well ... it runs the tests :)npm run test:watch- same as above but in a watch modenpm run bench- run the benchmark suite
This project is under MIT License [https://opensource.org/licenses/MIT]