Flexible RBAC Permission System with NestJS + Prisma
RBAC implementation in a SaaS system — data model design, Guard interception, decorator patterns, and API permission validation flow.
Permission System Design
In a SaaS membership system, different roles (super admin, operator, regular user) need different feature permissions.
Permission systems are B2B product infrastructure — nobody notices when it's right, everyone finds you when it's wrong.
Our requirements:
Data Model
Using Prisma to define User, Role, Permission tables with many-to-many relations for flexible permission assignment.
Core Table Design
Use `resource:action` format for permission identifiers (e.g., `user:create`) — semantically clear and supports wildcard matching.
Permission Inheritance
Support role hierarchy — super admins automatically have all permissions, department managers have all department permissions. Implemented via parent_role_id for recursive inheritance.
NestJS Guard Implementation
Custom `@Roles()` decorator and `RolesGuard` for unified API access control at the Controller layer.
Decorator Design
We defined two decorators:
Guard Execution Flow
Guards are the last line of defense, but shouldn't be the only one. Frontend should also hide unauthorized buttons and menus for better UX.
Caching Strategy
Redis Cache
Cache user permissions in Redis to avoid database queries per request. Key design:
Cache Invalidation
When admins modify role permissions:
Update role-permission associations in database
Find all users with that role
Batch-delete their Redis caches
Cache automatically rebuilds on next request
Watch performance during batch cache clearing — if a role has thousands of users, sequential deletion is slow. Use Redis pipeline for batch operations.
Frontend-Backend Sync
Backend returns permission list, frontend dynamically renders menus and buttons.
Frontend Permission Control
Login Flow
On successful login, return all permission identifiers at once. Frontend stores them in state management. Permission changes pushed via WebSocket.
Testing Strategy
Permission system testing is critical — one bug could mean data leakage.
Our principle: Permission-related code must have 100% test coverage. No exceptions.
Conclusion
RBAC seems simple but achieving flexibility, efficiency, and security isn't easy. Keys: extensible data model, smart caching, frontend-backend coordination. This system has been running stable for 1+ year in our SaaS, supporting 200+ customer organizations with different permission configs.
For simple needs (a few fixed roles), you don't need this complexity. Just hardcode role checks in Guards. An over-engineered permission system is more dangerous than none.