NestJS Cursor Rules: Enterprise Node.js Framework
Cursor rules for NestJS covering modules, dependency injection, controllers/providers, guards/pipes/interceptors, DTO validation, OpenAPI, and Jest testing.

Overview
NestJS brings Angular-inspired architecture to Node.js backends — modules, decorators, and dependency injection built on top of Express or Fastify. These cursor rules enforce modular structure, provider injection, guard/pipe/interceptor patterns, DTO validation with class-validator, OpenAPI documentation, and Jest testing conventions for clean, scalable TypeScript APIs.
Note:
Enforces feature modules, constructor-based DI, decorator-driven routing, guard/pipe/interceptor composition, class-validator DTOs, @nestjs/swagger OpenAPI docs, and Jest/TestingModule patterns.
Rules Configuration
---
description: Enforces NestJS enterprise patterns including modular architecture, dependency injection, decorator-driven controllers, guard/pipe/interceptor pipelines, DTO validation, and OpenAPI documentation. Provides guidelines for scalable TypeScript backends.
globs: **/*.ts
---
# NestJS Best Practices
You are an expert in NestJS, TypeScript backend development, and enterprise Node.js patterns.
You understand modular architecture, dependency injection, and production-grade API design.
### Project Structure
- /src/<domain> — one module per domain (posts, users, auth) containing:
- <domain>.module.ts — NestJS module with imports, controllers, providers
- <domain>.controller.ts — route handlers with decorators
- <domain>.service.ts — business logic, injected into controllers
- dto/ — Data Transfer Objects with class-validator decorators
- entities/ — TypeORM/Prisma entity definitions
- guards/ — auth/role guards specific to this domain
- <domain>.service.spec.ts — unit tests for the service
- <domain>.controller.spec.ts — integration tests for the controller
- /src/common — shared guards, interceptors, filters, pipes, decorators
### Modules & Dependency Injection
- One module per feature domain, imported into AppModule
- Use @Module() decorator with imports, controllers, providers, exports
- Use constructor-based injection with @Injectable()
- Register providers in module providers array, never use new manually
- Export providers from a module if other modules need them
- Use custom providers (useClass, useFactory, useValue) for configuration
### Controllers & Routing
- Use @Controller('path') with resource prefix
- Use @Get(), @Post(), @Put(), @Patch(), @Delete() HTTP method decorators
- Extract params with @Param(), query with @Query(), body with @Body()
- Use DTO classes as body types on @Body() for type safety
- Return response DTOs; never expose entity internals directly
- Use @HttpCode() for non-200 status codes
### Validation & Pipes
- Define DTOs with class-validator: @IsString(), @IsEmail(), @MinLength()
- Apply ValidationPipe globally in main.ts: app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))
- Use @IsOptional() and @ValidateNested() for optional/nested fields
- Create custom pipes with PipeTransform interface for domain-specific validation
- Parse route params with ParseIntPipe, ParseUUIDPipe
### Guards & Security
- Implement CanActivate interface for auth and role guards
- Use @UseGuards() at controller or route level
- Create JwtAuthGuard extending AuthGuard('jwt') for JWT validation
- Create RolesGuard using @SetMetadata('roles', ['admin']) + Reflector
- Hash passwords with bcrypt; never store plaintext
### Interceptors & Filters
- Use interceptors for response transformation, logging, caching
- Create exception filters implementing ExceptionFilter for custom error responses
- Apply global exception filter for consistent error format: { statusCode, message, timestamp, path }
- Use @UseInterceptors() for route-specific transformations
### OpenAPI
- Use @ApiTags() to group endpoints in Swagger docs
- Use @ApiOperation() for endpoint descriptions
- Use @ApiResponse() for documenting response types and status codes
- Use @ApiProperty() on DTO fields for request/response schema documentation
- Enable Swagger with SwaggerModule.createDocument()
### Testing
- Unit test services with Jest: create TestingModule with providers array
- Integration test controllers with supertest and TestingModule
- Mock dependencies with jest.fn() or custom providers using useValue
- Test guards, interceptors, and pipes in isolation
Installation
Create nestjs.mdc in your project's .cursor/rules/ directory and paste the configuration above. Cursor and Windsurf both read .cursor/rules/ — Copilot users place it in .github/copilot-instructions.md instead.
Examples
// src/posts/posts.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PostsController } from './posts.controller';
import { PostsService } from './posts.service';
import { Post } from './entities/post.entity';
@Module({
imports: [TypeOrmModule.forFeature([Post])],
controllers: [PostsController],
providers: [PostsService],
exports: [PostsService],
})
export class PostsModule {}
// src/posts/posts.controller.ts
import {
Controller, Get, Post, Body, Param, Query, UseGuards, HttpCode
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { PostsService } from './posts.service';
import { CreatePostDto } from './dto/create-post.dto';
import { PaginationQueryDto } from './dto/pagination-query.dto';
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
import { PostResponseDto } from './dto/post-response.dto';
@ApiTags('Posts')
@Controller('api/v1/posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'List published posts' })
@ApiResponse({ status: 200, type: [PostResponseDto] })
async findAll(@Query() query: PaginationQueryDto) {
return this.postsService.findAll(query);
}
@Post()
@UseGuards(JwtAuthGuard)
@HttpCode(201)
@ApiOperation({ summary: 'Create a new post' })
@ApiResponse({ status: 201, type: PostResponseDto })
@ApiResponse({ status: 422, description: 'Validation failed' })
async create(@Body() dto: CreatePostDto) {
return this.postsService.create(dto);
}
@Get(':id')
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Get a single post' })
async findOne(@Param('id') id: string) {
return this.postsService.findOne(id);
}
}
// src/posts/dto/create-post.dto.ts
import { IsString, IsBoolean, IsOptional, MinLength, MaxLength } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreatePostDto {
@ApiProperty({ example: 'My First Post', minLength: 1, maxLength: 200 })
@IsString()
@MinLength(1)
@MaxLength(200)
title: string;
@ApiProperty({ example: 'Post content goes here' })
@IsString()
@MinLength(1)
body: string;
@ApiProperty({ example: false, required: false })
@IsBoolean()
@IsOptional()
published?: boolean = false;
}
// src/common/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
if (!requiredRoles) return true;
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
Related Resources
Related Articles
Kotlin Cursor Rules: Android Development Best Practices
Cursor rules for Kotlin and Android enforcing Jetpack Compose, coroutines, and clean architecture. Helps AI generate secure, production-ready code with context.
Angular Cursor Rules: AI-Assisted Development Best Practices
Angular cursor rules for TypeScript and RxJS. Enforce clean architecture, component design, testing, and CLI workflows. Generate secure, maintainable code with complete project context.
Firebase Cursor Rules: Google Backend-as-a-Service
Cursor rules for Firebase covering Firestore, Authentication, Cloud Functions, Realtime Database, Storage, Hosting, Security Rules, and the emulator suite.