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
Python Cursor Rules: AI-Powered Development Best Practices
Cursor rules for Python development enforcing PEP 8 standards, modern Python features, and clean code principles with AI assistance for production-ready code.
Programming Languages Supported by Cursor Rules
Explore programming languages supported by Cursor Rules with language-specific guidelines, best practices, and examples for effective AI-assisted coding.
Nuxt Cursor Rules: Vue Full-Stack Framework Guide
Cursor rules for Nuxt development covering server-side rendering, auto-imports, modules, composables, and deployment strategies for universal Vue applications.