Quick Start • Usage • API Reference • FAQ • Contributing • License
Did you ever find yourself in a situation where you need to restrict access to certain documents in a collection? Tired of passing that user object just to save who updated a document?
nestjs-mongoose-dac
is a superset of @nestjs/mongoose
that introduces Document Access Control (DAC) capabilities. It provides tools to define and enforce access policies on Mongoose models, enabling fine-grained control over data access and manipulation.
npm install nestjs-mongoose-dac mongoose
If your project already uses @nestjs/mongoose
, please remove it first, as this package includes it internally.
Since this package is a superset of @nestjs/mongoose
all configurations are the same, MongooseModule#forRoot
, MongooseModule#forRootAsync
, MongooseModule#forFeature
, etc.
// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from 'nestjs-mongoose-dac';
@Module({
imports: [MongooseModule.forRoot('mongodb://localhost:27017/mydb')],
})
export class AppModule {}
// cats.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from 'nestjs-mongoose-dac';
import { Cat, CatSchema } from './cat.schema';
@Module({
imports: [MongooseModule.forFeature([{ name: 'Cat', schema: CatSchema }])],
})
export class CatsModule {}
Rules can be defined using the defineRule
function.
import { Document, Types } from 'mongoose';
import { defineRule, Prop, Schema, SchemaFactory } from 'nestjs-mongoose-dac';
@Schema()
export class Cat extends Document {
@Prop()
name: string;
@Prop()
age: number;
@Prop()
breed: string;
@Prop()
ownerId: string;
}
export const CatSchema = SchemaFactory.createForClass(Cat);
defineRule(CatSchema, 'ownerValidation', {
type: ['query'],
rule: (get) => ({ ownerId: get('ownerId') }),
});
EnrichmentsService
allows you to set and retrieve contextual data for access control rules.
The service uses AsyncLocalStorage
to store the data per request lifecycle.
- Add enrichments - preferably in a middleware/interceptor, or wherever you process incoming requests
import { Injectable } from '@nestjs/common';
import { EnrichmentsService } from 'nestjs-mongoose-dac';
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Request } from 'express';
@Injectable()
export class CatOwnerInterceptor implements NestInterceptor {
constructor(private readonly enrichmentsService: EnrichmentsService) {}
intercept(context: ExecutionContext, next: CallHandler<any>) {
const request = context.switchToHttp().getRequest<Request>();
const ownerId = request.cookies['ownerId'];
this.enrichmentsService.setGlobalEnrichment('ownerId', ownerId);
// or set it per schema
this.enrichmentsService.setEnrichment(Cat.name, 'ownerId', ownerId);
return next.handle();
}
}
- Use the data in rules - use the provided
get
function for retriving enrichments
defineRule(CatSchema, 'ownerValidation', {
type: ['query'],
rule: (get) => ({ ownerId: get('ownerId') }),
});
For detailed API reference of @nestjs/mongoose
, please refer to the official NestJS Mongoose documentation.
defineRule(schema: Schema, key: string, policy: AccessPolicyRule): void
- Defines an access policy for a Mongoose schema.
schema
: The Mongoose schema to which the rule applies.key
: The name of the rule - should be unique per schema.policy
: The access policy rule object, which includes:type
: A singleRuleType
or an array ofRuleType
s, each indicating a type of operation. Supported types and corresponding operations are:'query'
:find
,findOne
,distinct
'count'
:countDocuments
,estimatedDocumentCount
'update'
:updateOne
,updateMany
,findOneAndUpdate
,replaceOne
,findOneAndReplace
'delete'
:deleteOne
,deleteMany
,findOneAndDelete
'save'
:save
(document-level),insertMany
rule
: A function that receives aget
function (to retrieve dynamic values like user ID) and returns an object specifying field-level constraints:- For
query
andcount
types: values can be plain values or MongoDB query selectors (e.g.,$eq
,$gte
,$in
). - For all other types: only plain values are allowed, matching the field's original type in the schema.
- For
-
setEnrichment(schemaName: string, key: string, value: unknown)
- Sets an enrichment value for a specific schema.
schemaName
: The name of the schema to which the enrichment applies.key
: The key for the enrichment value.value
: The value to set.
-
setGlobalEnrichment(key: string, value: unknown)
- Sets a global enrichment value - for all schemas to use in rules.
key
: The key for the enrichment value.value
: The value to set.
@nestjs/mongoose
is the base package for working with Mongoose in NestJS. However, it does not enhances or offers more "fullstack" tools to ease the work with the database. This package extends @nestjs/mongoose
to add access-rules capabilities, enrichments and more, making it easier to create and manage complex workflows.
The defineRule
function allows you to define access policies for Mongoose schemas. Each rule can specify the type of operations it applies to (e.g., query, update, delete) and the logic for determining access. The rules are enforced automatically during Mongoose operations using Mongoose pre
hook.
The EnrichmentsService
uses AsyncLocalStorage
to store enrichment data per request lifecycle. This allows you to set contextual data for access control rules without passing it explicitly through the request.
Contributions welcome! Read the contributing guidelines to get started.
🐛 Issues
MIT
Keywords: nestjs, mongoose, dac, document-access-control, nestjs mongoose, mongo, mongodb