--- name: nestjs description: NestJS architecture including modules, dependency injection, guards, interceptors, and microservices patterns. allowed-tools: Read, Write, Edit, Bash, Glob, Grep --- # NestJS Skill Expert assistance for building enterprise Node.js applications with NestJS. ## Capabilities - Design modular NestJS applications - Implement dependency injection patterns - Create guards, pipes, and interceptors - Build microservices architectures - Configure Swagger/OpenAPI documentation - Set up testing with Jest ## Usage Invoke this skill when you need to: - Build enterprise-grade APIs - Implement microservices - Create modular architecture - Add authentication/authorization - Generate API documentation ## Inputs | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | moduleName | string | Yes | Module name | | features | array | No | guards, pipes, interceptors | | database | string | No | prisma, typeorm, mongoose | | microservices | boolean | No | Enable microservices | ## Architecture Patterns ### Module Structure ```typescript // src/users/users.module.ts import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; import { PrismaModule } from '../prisma/prisma.module'; @Module({ imports: [PrismaModule], controllers: [UsersController], providers: [UsersService], exports: [UsersService], }) export class UsersModule {} ``` ### Controller with DTOs ```typescript // src/users/users.controller.ts import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards, HttpCode, HttpStatus, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse } from '@nestjs/swagger'; import { UsersService } from './users.service'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; import { UserResponseDto } from './dto/user-response.dto'; import { PaginationDto } from '../common/dto/pagination.dto'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { RolesGuard } from '../auth/guards/roles.guard'; import { Roles } from '../auth/decorators/roles.decorator'; import { CurrentUser } from '../auth/decorators/current-user.decorator'; @ApiTags('users') @Controller('users') @UseGuards(JwtAuthGuard, RolesGuard) @ApiBearerAuth() export class UsersController { constructor(private readonly usersService: UsersService) {} @Get() @ApiOperation({ summary: 'Get all users' }) @ApiResponse({ status: 200, type: [UserResponseDto] }) findAll(@Query() pagination: PaginationDto) { return this.usersService.findAll(pagination); } @Get(':id') @ApiOperation({ summary: 'Get user by ID' }) @ApiResponse({ status: 200, type: UserResponseDto }) findOne(@Param('id') id: string) { return this.usersService.findById(id); } @Post() @Roles('admin') @ApiOperation({ summary: 'Create user' }) @ApiResponse({ status: 201, type: UserResponseDto }) create(@Body() createUserDto: CreateUserDto) { return this.usersService.create(createUserDto); } @Put(':id') @ApiOperation({ summary: 'Update user' }) update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { return this.usersService.update(id, updateUserDto); } @Delete(':id') @Roles('admin') @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Delete user' }) remove(@Param('id') id: string) { return this.usersService.delete(id); } } // src/users/dto/create-user.dto.ts import { IsEmail, IsString, MinLength, IsOptional, IsEnum } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class CreateUserDto { @ApiProperty({ example: 'John Doe' }) @IsString() name: string; @ApiProperty({ example: 'john@example.com' }) @IsEmail() email: string; @ApiProperty({ minLength: 8 }) @IsString() @MinLength(8) password: string; @ApiProperty({ required: false, enum: ['user', 'admin'] }) @IsOptional() @IsEnum(['user', 'admin']) role?: string; } ``` ### Service with Dependency Injection ```typescript // src/users/users.service.ts import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; import { PaginationDto } from '../common/dto/pagination.dto'; import * as bcrypt from 'bcryptjs'; @Injectable() export class UsersService { constructor(private readonly prisma: PrismaService) {} async findAll(pagination: PaginationDto) { const { page = 1, limit = 10, search } = pagination; const skip = (page - 1) * limit; const [users, total] = await Promise.all([ this.prisma.user.findMany({ where: search ? { name: { contains: search, mode: 'insensitive' } } : undefined, skip, take: limit, select: { id: true, name: true, email: true, role: true, createdAt: true, }, }), this.prisma.user.count({ where: search ? { name: { contains: search, mode: 'insensitive' } } : undefined, }), ]); return { data: users, meta: { total, page, limit, totalPages: Math.ceil(total / limit), }, }; } async findById(id: string) { const user = await this.prisma.user.findUnique({ where: { id }, select: { id: true, name: true, email: true, role: true, createdAt: true, }, }); if (!user) { throw new NotFoundException(`User with ID ${id} not found`); } return user; } async create(dto: CreateUserDto) { const existing = await this.prisma.user.findUnique({ where: { email: dto.email }, }); if (existing) { throw new ConflictException('Email already in use'); } const hashedPassword = await bcrypt.hash(dto.password, 10); return this.prisma.user.create({ data: { ...dto, password: hashedPassword, }, select: { id: true, name: true, email: true, role: true, createdAt: true, }, }); } async update(id: string, dto: UpdateUserDto) { await this.findById(id); if (dto.password) { dto.password = await bcrypt.hash(dto.password, 10); } return this.prisma.user.update({ where: { id }, data: dto, select: { id: true, name: true, email: true, role: true, createdAt: true, }, }); } async delete(id: string) { await this.findById(id); await this.prisma.user.delete({ where: { id } }); } } ``` ### Guards and Decorators ```typescript // src/auth/guards/jwt-auth.guard.ts import { Injectable, ExecutionContext } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { Reflector } from '@nestjs/core'; import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { constructor(private reflector: Reflector) { super(); } canActivate(context: ExecutionContext) { const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) { return true; } return super.canActivate(context); } } // src/auth/guards/roles.guard.ts import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { ROLES_KEY } from '../decorators/roles.decorator'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.getAllAndOverride(ROLES_KEY, [ context.getHandler(), context.getClass(), ]); if (!requiredRoles) { return true; } const { user } = context.switchToHttp().getRequest(); return requiredRoles.includes(user.role); } } // src/auth/decorators/roles.decorator.ts import { SetMetadata } from '@nestjs/common'; export const ROLES_KEY = 'roles'; export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles); // src/auth/decorators/current-user.decorator.ts import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const CurrentUser = createParamDecorator( (data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); return request.user; }, ); ``` ### Interceptors ```typescript // src/common/interceptors/transform.interceptor.ts import { Injectable, NestInterceptor, ExecutionContext, CallHandler, } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export interface Response { data: T; meta?: Record; } @Injectable() export class TransformInterceptor implements NestInterceptor> { intercept(context: ExecutionContext, next: CallHandler): Observable> { return next.handle().pipe( map((data) => { if (data?.data && data?.meta) { return data; } return { data }; }), ); } } ``` ## Best Practices - Use modules for feature encapsulation - Leverage dependency injection - Implement DTOs for validation - Use guards for authorization - Generate OpenAPI documentation ## Target Processes - enterprise-api-development - microservices-architecture - backend-development - nestjs-application