@trishchuk/nest-casl
v1.1.0
Published
Declarative role-based access control for NestJS with CASL. Supports REST, GraphQL, WebSocket, conditional permissions, field-level restrictions, multi-tenant context, and dynamic DB-driven rules.
Maintainers
Readme
@trishchuk/nest-casl
Declarative, role-based access control for NestJS powered by CASL. Works with REST, GraphQL and WebSocket.
Installation
npm install @trishchuk/nest-caslPeer dependencies: @nestjs/core, @nestjs/common (>= 7.0.0). Optional: @nestjs/graphql, @nestjs/apollo.
Quick Start
// 1. Define roles
export enum Roles { admin = 'admin', editor = 'editor', viewer = 'viewer' }
// 2. Configure root module
@Module({
imports: [
CaslModule.forRoot<Roles>({
superuserRole: Roles.admin,
getUserFromRequest: (request) => request.user,
}),
],
})
export class AppModule {}
// 3. Define permissions per feature
const permissions: Permissions<Roles, Post, Actions> = {
everyone({ can }) {
can(Actions.read, Post);
},
editor({ user, can }) {
can(Actions.update, Post, { authorId: user.id });
},
};
@Module({
imports: [CaslModule.forFeature({ permissions })],
})
export class PostModule {}
// 4. Protect endpoints
@Put(':id')
@UseGuards(AccessGuard)
@UseAbility(Actions.update, Post, PostHook)
async updatePost(@Param('id') id: string, @Body() input: UpdatePostInput) {
return this.postService.update(id, input);
}
// 5. Use conditions for filtering
@Get()
@UseGuards(AccessGuard)
@UseAbility(Actions.read, Post)
async posts(@CaslFilter() filter: FindOptionsWhere<Post>) {
return this.postService.findAll({ where: filter });
}Documentation
| Guide | Description |
|-------|-------------|
| Configuration | forRoot, forRootAsync, forFeature, async/DB permissions, multi-tenant context |
| Permissions | Defining roles, conditions, inheritance, custom actions, multi-tenant |
| Conditions | toFilter, toWhere, toSql, toMongo — choosing the right method, ORM integration |
| Hooks | Subject hooks, user hooks, paramKey, execution flow |
| Decorators | @UseAbility, @CaslConditions, @CaslFilter, @CaslSubject, @CaslUser |
| Examples | REST API, multi-tenant SaaS, scope-based, field-level, GraphQL with DB, testing |
| Migration | From nest-casl or custom CASL implementation |
| API Reference | Complete type signatures for all exports |
Key Features
- Role-based permissions with
can(),cannot(),extend()andeveryone/every - Conditional access — ownership checks like
{ userId: user.id }with automatic subject fetching via hooks - Multi-transport — HTTP, GraphQL, WebSocket via unified
ContextProxy - Conditions as queries — convert CASL rules to SQL, MongoDB, or plain filter objects via
@CaslConditions()/@CaslFilter() - Multi-tenant support — pass tenant context to permission builders via
getContextFromRequest - Dynamic permissions — load rules from database at runtime via
onBuildAbilityasync hook - Custom ConditionsProxy —
conditionsProxyFactoryfor role-specific proxy behavior (e.g., admin gets no filter) - Field-level restrictions —
cannot(action, subject, ['field'])with customizablegetFieldsFromRequest - Full DI integration — root options registered as global NestJS provider, not global mutable state
Comparison with nest-casl
This package is a fork of nest-casl with bug fixes, new features, and architectural improvements. See Migration Guide for a step-by-step upgrade path.
| Feature | nest-casl | @trishchuk/nest-casl |
|---------|-----------|---------------------|
| Decorators | @UseAbility, @CaslConditions, @CaslSubject, @CaslUser | All of the above + @CaslFilter() |
| Conditions output | toSql(), toMongo(), toAst() | All of the above + toFilter(), toWhere(), toQuery(), getRules() |
| Permission context | user only | user + custom context via getContextFromRequest |
| Multi-tenant support | Manual workaround | Built-in via context in permission builders |
| DB-driven permissions | Not supported | onBuildAbility async hook in forFeature() |
| Module-scoped metadata | Not supported | moduleName + subjectsMap in forFeature() |
| Custom ConditionsProxy | Requires replacing entire guard | conditionsProxyFactory option in forRoot() |
| Field extraction | Hardcoded flatten(body) | Customizable via getFieldsFromRequest |
| @UseAbility options | (action, subject, hook) | Also accepts { hook, paramKey } object |
| Root config storage | Global mutable state (Reflect.defineMetadata) | NestJS DI provider (+ legacy fallback) |
| AccessService methods | Synchronous | Async (supports onBuildAbility hooks) |
| Internal architecture | Single monolithic AccessService | Decomposed: AbilityResolver, AccessEvaluator, FieldAccessChecker |
| CASL type imports | @casl/ability/dist/types/types (private) | Local types.ts (no private path dependency) |
| AccessService export from forFeature | Missing (bug #905) | Fixed |
| Subject hook with mixed conditions | Broken (bug #923) | Fixed |
| ConditionsProxy stale user | Uses pre-hook user | Uses post-hook user |
License
MIT
