Add a new API endpoint

Add a new API endpoint to an existing module following a layered architecture (contract, driver, use-case, DTO, controller, module).

Sby Skills Guide Bot
DevelopmentIntermediate
007/27/2026
#api#endpoint#nestjs#backend#development

Recommended for

Skill: new-endpoint

Add a new API endpoint to an existing module.

TRIGGER: When the user asks to add a new endpoint, route, or API action to an existing module (e.g., "add a deactivate user endpoint", "create an endpoint to bulk import suppliers").

Instructions

1. Read Before Writing

Always read the existing controller, use-cases, drivers, and contract files before making changes. Understand the current patterns and avoid duplicating existing functionality.

2. Layer-by-Layer (Bottom Up)

Add code in this order:

a) Contract (if new operation is needed)

  • Add the method signature to the driver interface in contracts/<domain>-driver.contract.ts.
  • Type it with the Zod-derived DTO classes from dto/ — request DTOs (Create*RequestDto, Update*RequestDto) for input, *ResponseDto for the return. Do not introduce hand-written *Data interfaces; the DTO is the single source of truth.

b) Driver (if new DB query is needed)

  • Add the method implementation in drivers/prisma-<domain>.driver.ts.
  • Use Prisma query methods and map the row to the response DTO via the driver's to<Entity>Response mapper (timestamps → .toISOString(), omit deletedAt).

c) Use-Case (for business logic)

  • Create a new use-case file: use-cases/<verb>-<entity>.use-case.ts.
  • One class per action with an execute() method.
  • Inject drivers via DI tokens: @Inject(<DOMAIN>_DRIVER).
  • Throw appropriate NestJS exceptions: NotFoundException, ConflictException, BadRequestException, ForbiddenException.

d) DTO (if endpoint accepts a body or custom query params)

  • Create a new DTO file in dto/ using Zod schema + createZodDto().
  • Reuse PaginationDto from src/common/dto/pagination.dto.ts for paginated list endpoints.
  • Follow naming: <verb>-<entity>.dto.ts (e.g., deactivate-user.dto.ts).
  • See /new-dto skill for full DTO conventions and Zod reference.

e) Controller

  • Add the route handler method.
  • Inject the new use-case in the constructor.
  • Apply decorators in this order: @Public() or @Roles() or @Permissions(), then HTTP method (@Get, @Post, @Patch, @Delete), then @HttpCode (if not default), then Swagger decorators.
  • Always include @ApiOperation, @ApiResponse for success and error cases.
  • Use @ApiParam for path parameters.
  • Use ParseUUIDPipe on all :id path parameters: @Param('id', ParseUUIDPipe) id: string.

f) Module

  • Register the new use-case in the module's providers array.

3. HTTP Method Conventions

| Action | Method | Route | Status | |--------|--------|-------|--------| | List | GET | / | 200 | | Get one | GET | /:id | 200 | | Create | POST | / | 201 | | Update | PATCH | /:id | 200 | | Delete | DELETE | /:id | 200 | | Custom action | POST | /:id/<action> | 200 |

4. Response Handling

All responses are automatically wrapped by TransformInterceptor into { statusCode, data, timestamp }. Do NOT manually wrap responses. Just return the data.

5. Auth Decorators

  • @Public() — skip JWT auth entirely (login, register, public endpoints).
  • @Roles(DomainRole.ADMIN) — require specific role.
  • @Permissions('domain.action') — require specific permission.
  • No decorator = authenticated user (JWT required, any role).

6. Verify

yarn lint         # Fix any linting issues
yarn test         # Ensure nothing is broken
Related skills