Install
openclaw skills install nestjs-dev-guideNestJS Node.js server-side framework development guide.
openclaw skills install nestjs-dev-guideNestJS is a progressive framework for building efficient, scalable Node.js server-side applications. Built with TypeScript, it integrates OOP/FP/FRP, uses Express (default) or Fastify under the hood, and its architecture is deeply inspired by Angular.
Version: v11+ | Requires Node.js ≥20
Use when the user needs to build a backend application with NestJS, create REST APIs, use decorators and dependency injection, build microservices, configure Swagger documentation, or write NestJS Controllers/Providers/Modules/Guards/Pipes/Interceptors. Trigger keywords include "nest", "nestjs", "create API", "backend service", "decorator injection", "guard", "pipe", "interceptor", "swagger", etc.
npm i -g @nestjs/cli
nest new my-project
cd my-project && npm run start:dev
Or use npx @nestjs/cli@latest new my-project to avoid global installation.
src/
├── main.ts # Application entry, NestFactory.create()
├── app.module.ts # Root module
├── app.controller.ts # Base controller
├── app.controller.spec.ts
└── app.service.ts # Base service
The @Module() decorator defines modules — the logical boundaries and DI container scopes of the application.
@Module({
imports: [OtherModule], // Import other modules
controllers: [CatsController], // Register controllers
providers: [CatsService], // Register providers
exports: [CatsService], // Export providers for use by other modules
})
export class CatsModule {}
CatsModule, UsersModuleexports array@Global() decorator, but not recommended to overusestatic forRoot() returns a runtime-configured moduleexports@Controller('prefix') + HTTP method decorators define routes:
@Controller('cats')
export class CatsController {
@Get() findAll() {}
@Get(':id') findOne(@Param('id') id: string) {}
@Post() create(@Body() dto: CreateCatDto) {}
@Put(':id') update(@Param('id') id: string, @Body() dto: UpdateCatDto) {}
@Delete(':id') remove(@Param('id') id: string) {}
}
Parameter Decorators Cheat Sheet:
| Decorator | Extracts |
|---|---|
@Param(key?) | Route parameters |
@Body(key?) | Request body |
@Query(key?) | Query parameters |
@Headers(name?) | Request headers |
@Req() | Native Request object |
@Res() | Native Response (manual management required) |
@Ip() | Client IP |
@HostParam() | Subdomain parameters |
Route wildcards: @Get('ab{*splat}cd'), @Get('prefix/*')
Status codes and headers: @HttpCode(204), @Header('Cache-Control', 'none')
Redirect: @Redirect('url', 301) or return { url, statusCode }
DTO Definition: Use class (not interface), because interfaces disappear after compilation and cannot provide runtime type metadata:
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
Classes marked with @Injectable(), injected via constructor:
@Injectable()
export class CatsService {
private cats: Cat[] = [];
create(cat: Cat) { this.cats.push(cat); }
findAll(): Cat[] { return this.cats; }
}
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {} // Auto-injected
}
Custom Providers (using full syntax in @Module.providers):
| Syntax | Purpose |
|---|---|
{ provide: Token, useClass: Class } | Standard class provider |
{ provide: Token, useValue: value } | Constant value / mock object |
{ provide: Token, useFactory: fn, inject: [...] } | Factory function |
{ provide: Token, useExisting: OtherToken } | Alias |
Scope: Default SINGLETON (application-level singleton), can be set to REQUEST (request-level).
Optional Providers: @Optional() decorator, no error if dependency is missing.
Plain Express middleware class implementing NestMiddleware, bound in consumer:
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) { /* ... */ next(); }
}
// Apply in module
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('cats');
}
}
Implements PipeTransform, performs validation and/or data transformation. Execution order: Middleware → Guards → Interceptor(pre) → Pipe → Handler → Interceptor(post) → Exception Filter.
Built-in pipes: ValidationPipe, ParseIntPipe, ParseBoolPipe, ParseArrayPipe, ParseUUIDPipe
npm i class-validator class-transformer
// main.ts — global enable
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // Automatically strip undeclared properties
forbidNonWhitelisted: true, // Error if non-whitelisted properties exist
transform: true, // Auto type conversion (string→number etc.)
disableErrorMessages: false,
}));
// DTO using validation decorators
import { IsString, IsInt, IsEmail, Min, Max } from 'class-validator';
export class CreateUserDto {
@IsString() @IsNotEmpty()
name: string;
@IsInt() @Min(0) @Max(150)
age: number;
@IsEmail()
email: string;
}
Zod validation alternative:
const schema = z.object({ name: z.string(), age: z.number() });
@UsePipes(new ZodValidationPipe(schema))
Implements CanActivate, returns boolean determining whether to proceed. Executes after middleware and before interceptors/pipes.
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}
Role guard + custom metadata:
// Create decorator
import { Reflector } from '@nestjs/core';
export const Roles = Reflector.createDecorator<string[]>();
// Usage
@Post()
@Roles(['admin'])
async create() {}
// Read in guard
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get(Roles, context.getHandler());
if (!roles) return true;
const { user } = context.switchToHttp().getRequest();
return roles.some(r => user.roles?.includes(r));
}
}
Binding scope: @UseGuards(RolesGuard) on class/method; app.useGlobalGuards() global; via APP_GUARD token for DI.
Implements NestInterceptor. Inserts logic before/after methods, can transform results/exceptions.
// Logging interceptor
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(ctx: ExecutionContext, next: CallHandler): Observable<any> {
const now = Date.now();
return next.handle().pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`))
);
}
}
// Uniform response wrapper
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, {data: T}> {
intercept(ctx: ExecutionContext, next: CallHandler) {
return next.handle().pipe(map(data => ({ data })));
}
}
// Built-in exceptions
throw new BadRequestException('message');
throw new NotFoundException();
throw new UnauthorizedException();
// 20+ types: Forbidden, Conflict, InternalServerError, BadGateway, ...
// Custom
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
throw new HttpException({ status: 403, error: 'custom' }, 403);
Custom filter:
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const res = ctx.getResponse<Response>();
const status = exception.getStatus();
res.status(status).json({ statusCode: status, timestamp: new Date().toISOString() });
}
}
nest new <name> # Create new project
nest g resource <name> # Generate complete CRUD resource (module/controller/service/dto)
nest g module <name> # Generate module
nest g controller <name> # Generate controller
nest g service <name> # Generate service
nest g guard <name> # Generate guard
nest g pipe <name> # Generate pipe
nest g interceptor <name># Generate interceptor
nest g filter <name> # Generate filter
nest build # Compile
nest start # Start
nest info # System information
npm i @nestjs/swagger
// main.ts
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
const config = new DocumentBuilder()
.setTitle('API Docs').setVersion('1.0').addBearerAuth().build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, documentFactory);
// Access http://localhost:3000/api
Decorators: @ApiTags(), @ApiOperation(), @ApiProperty(), @ApiResponse(), @ApiBearerAuth()
Nest recommends using TypeORM (@nestjs/typeorm) or Prisma:
npm i @nestjs/typeorm typeorm pg
// app.module.ts
TypeOrmModule.forRoot({ type: 'postgres', host: 'localhost', database: 'mydb', autoLoadEntities: true, synchronize: true })
// feature module
TypeOrmModule.forFeature([User])
npm i @nestjs/config
// app.module.ts
ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env' })
// Usage
constructor(private configService: ConfigService) {}
this.configService.get<string>('DATABASE_URL')
npm i @nestjs/schedule
@Cron('45 * * * * *') handleCron() {}
@Interval(10000) handleInterval() {}
@Timeout(5000) handleTimeout() {}
npm i @nestjs/cache-manager cache-manager
CacheModule.register() + @UseInterceptors(CacheInterceptor)
Most Nest components support two global registration approaches:
// Method 1: Register directly in main.ts (cannot inject dependencies)
app.useGlobalPipes(new ValidationPipe());
app.useGlobalGuards(new RolesGuard());
app.useGlobalInterceptors(new LoggingInterceptor());
app.useGlobalFilters(new HttpExceptionFilter());
// Method 2: Register via Token in AppModule (supports DI)
providers: [
{ provide: APP_PIPE, useClass: ValidationPipe },
{ provide: APP_GUARD, useClass: RolesGuard },
{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },
{ provide: APP_FILTER, useClass: HttpExceptionFilter },
]
1. Middleware
2. Guard
3. Interceptor (pre)
4. Pipe
5. Route Handler
6. Interceptor (post)
7. Exception Filter (if exception occurs)
src/
├── common/ # Shared: decorators, filters, guards, interceptors, pipes
├── config/ # Configuration
├── modules/
│ ├── users/
│ │ ├── dto/
│ │ ├── entities/
│ │ ├── users.controller.ts
│ │ ├── users.service.ts
│ │ └── users.module.ts
│ └── auth/
│ ├── dto/
│ ├── strategies/
│ ├── auth.controller.ts
│ ├── auth.service.ts
│ └── auth.module.ts
├── app.module.ts
└── main.ts
Detailed API reference see references/api-reference.md.