Fastify
rbacPlugin, registered once, declared per-route via config: { rbac: { resource, action } } — import { rbacPlugin } from 'rbac-fs/fastify'.
Usage
import Fastify from 'fastify';
import { RBAC } from 'rbac-fs';
import { rbacPlugin } from 'rbac-fs/fastify';
const rbac = new RBAC({ tenantId: 'acme-corp' });
await rbac.createRole('manager', { permissions: [{ resource: 'invoice', actions: ['approve'] }] });
const app = Fastify();
app.addHook('onRequest', async (request) => {
const role = request.headers['x-user-role'];
if (role) request.user = { id: 'demo-user', role };
});
await app.register(rbacPlugin, { rbac });
app.post(
'/invoices/:id/approve',
{ config: { rbac: { resource: 'invoice', action: 'approve' } } },
async (request) => ({ approved: request.params.id }),
);Register
rbacPlugin at the root app, not inside an encapsulated sub-plugin. Fastify's plugin encapsulation means a hook registered inside a child context never reaches sibling routes registered elsewhere — rbacPlugin is wrapped in fastify-plugin specifically to opt out of that and apply app-wide, the same approach @fastify/jwt uses internally.Full runnable version: examples/09-fastify-plugin.mjs.