Agent-skills-standard nestjs-controllers-services

Separate Controllers from Services and build Custom Decorators in NestJS. Use when defining NestJS controllers, services, or custom parameter decorators. (triggers: **/*.controller.ts, **/*.service.ts, Controller, Injectable, ExecutionContext, createParamDecorator)

install
source · Clone the upstream repo
git clone https://github.com/HoangNguyen0403/agent-skills-standard
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/HoangNguyen0403/agent-skills-standard "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/nestjs/nestjs-controllers-services" ~/.claude/skills/hoangnguyen0403-agent-skills-standard-nestjs-controllers-services && rm -rf "$T"
manifest: skills/nestjs/nestjs-controllers-services/SKILL.md
source content

NestJS Controllers & Services Standards

Priority: P0 (FOUNDATIONAL)

Controllers

  • Role: Handler only. Delegate all logic to Services.
  • Context:
    ExecutionContext
    helpers (
    switchToHttp()
    ) for platform-agnostic code.
  • Custom Decorators:
  • Avoid:
    @Request() req
    ->
    req.user
    (Not type-safe).
  • Pattern: Create typed decorators like
    @CurrentUser()
    ,
    @DeviceIp()
    .
import { RequestWithUser } from 'src/common/interfaces/request.interface';

export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext): User => {
    const request = ctx.switchToHttp().getRequest<RequestWithUser>();
    return request.user;
  },
);

DTOs & Validation

  • Strictness:
  • whitelist: true
    : Strip properties without decorators.
  • Critical:
    forbidNonWhitelisted: true
    : Throw error if unknown properties exist.
  • Transformation:
  • transform: true
    : Auto-convert primitives (String '1' -> Number 1) and instantiate DTO classes.
  • Documentation:
  • Swagger Plugin:
    @nestjs/swagger
    CLI plugin in
    nest-cli.json
    auto-detects DTO properties — no manual
    @ApiProperty()
    .

Interceptors (Response Mapping)

  • Map responses in Interceptors, not Controllers.
  • map()
    wraps success responses (e.g.
    { data: T }
    ).
  • See API Standards for
    PageDto
    and
    ApiResponse
    .
  • catchError()
    maps low-level errors (DB constraints) to
    HttpException
    (e.g.
    ConflictException
    ) before global filter.

Services & Business Logic

  • Singleton: Default.
  • Stateless: No request-specific state in class properties unless
    Scope.REQUEST
    .

Pipes & Validation

  • Global: Register
    ValidationPipe
    globally.
  • Route Params: Fail fast. Always use
    ParseIntPipe
    ,
    ParseUUIDPipe
    on all ID parameters.
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) { ... }

Lifecycle Events

  • Init: Use
    OnModuleInit
    for connection setup.
  • Destroy: Use
    OnApplicationShutdown
    for cleanup. (Requires
    enableShutdownHooks()
    ).

Anti-Patterns

  • No business logic in controllers: Delegate everything to Services; controllers only parse and respond.
  • No req.user access: Create typed
    @CurrentUser()
    decorator instead of accessing raw
    req
    .
  • No REQUEST scope by default: Use SINGLETON; it makes entire injection chain request-scoped.

References