This document covers the standardization of API responses, Swagger documentation patterns, and request context implementation in the Portfolio API.
All API responses follow a standardized format that includes:
- Status:
successorerror - Request ID: Unique identifier for tracking and debugging (48-bit, 12 hex chars)
- Data: Response payload (for success responses)
- Error: Error information (for error responses)
- Meta: Optional metadata, including pagination information
{
"status": "success",
"request_id": "req_88229911aabb",
"data": {
"id": 1,
"title": "Project Title",
"description": "Project description",
"created_at": "2026-01-08T14:00:00Z"
}
}{
"status": "success",
"request_id": "req_88229911aabb",
"data": [
{ "id": 1, "title": "Project 1" },
{ "id": 2, "title": "Project 2" }
],
"meta": {
"pagination": {
"page": 1,
"limit": 10,
"total_items": 45,
"total_pages": 5
}
}
}{
"status": "error",
"request_id": "req_99bbaacc",
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": {
"email": "must be a valid email",
"password": "is too short (minimum 8 characters)"
},
"path": "/api/auth/register",
"timestamp": "2026-01-08T14:05:00Z"
}
}Current Implementation: Uses crypto.randomBytes(6) to generate 48-bit (12 hex character) request IDs via RequestIdMiddleware.
Why 48 bits?
- Collision Resistance: ~281 trillion possibilities → ~16 million requests for 50% collision probability
- Production-Ready: Suitable for high-traffic production APIs
- Readable: 12 hex chars (e.g.,
req_88229911aabb) - short enough to communicate - Secure: Cryptographically secure random number generator
Format: req_ prefix + 12 hex characters
Location: src/core/middleware/request-id.middleware.ts
Implementation: Global NestJS interceptor that automatically wraps all responses in standardized format.
Location: src/core/interceptors/response-transform.interceptor.ts
Benefits:
- DRY Principle: No need to wrap every response manually
- Consistency: All responses automatically follow the same format
- Maintainability: Changes to response format only need to be made in one place
- Separation of Concerns: Controllers focus on business logic
Implementation: Custom exception filter that catches all exceptions and formats them consistently.
Location: src/core/exceptions/global-exception.filter.ts
Benefits:
- Centralized error handling
- Consistent error format across all endpoints
- Security: Prevents leaking sensitive error details
- Observability: Automatically includes request_id, path, and timestamp
Implementation: Snake_case format (total_items, total_pages) following REST API conventions.
Components:
PaginationUtil(src/core/utils/pagination.util.ts): Reusable pagination utilityPaginatedResponseDto(src/core/dto/paginated-response.dto.ts): Base class for paginated responses
Since all responses follow a standardized format, we use reusable decorators to automatically document API responses without manually adding @ApiResponse decorators to every endpoint.
For endpoints that return a single resource (GET /:id).
@ApiGetSingleResource(200, 'Returns a single project', SingleProjectResponseDto)
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number): Promise<SingleProjectResponseDto> {
return this.projectsService.findOne(id);
}Automatically documents:
- Success response (200)
- Not Found (404)
- Bad Request (400)
- Unauthorized (401)
- Forbidden (403)
- Internal Server Error (500)
For endpoints that return a paginated list (GET /).
@ApiGetPaginatedList('Returns a paginated list of projects', ProjectsListResponseDto)
@Get()
async findAll(@Query() query: FindAllProjectsQueryDto): Promise<ProjectsListResponseDto> {
return this.projectsService.findAll(query);
}Automatically documents:
- Success response (200) with pagination metadata
- Bad Request (400)
- Unauthorized (401)
- Forbidden (403)
- Internal Server Error (500)
For POST endpoints that create a resource.
@ApiCreateResource(201, 'Project created successfully', SingleProjectResponseDto)
@Post()
async create(@Body() createProjectDto: CreateProjectDto): Promise<SingleProjectResponseDto> {
return this.projectsService.create(createProjectDto);
}For PATCH/PUT endpoints that update a resource.
@ApiUpdateResource('Project updated successfully', SingleProjectResponseDto)
@Patch(':id')
async update(
@Param('id', ParseIntPipe) id: number,
@Body() updateProjectDto: UpdateProjectDto
): Promise<SingleProjectResponseDto> {
return this.projectsService.update(id, updateProjectDto);
}For DELETE endpoints.
@ApiDeleteResource('Project deleted successfully', DeleteResponseDto)
@Delete(':id')
async remove(@Param('id', ParseIntPipe) id: number): Promise<DeleteResponseDto> {
return this.projectsService.remove(id);
}- Less Boilerplate: Reduced from 20+ lines to 1 line per endpoint
- Consistency: All endpoints automatically get standard error documentation
- Maintainability: Update standard responses in one place
- Better DX: Easier to read and understand controller code
- Complete Documentation: All responses properly documented in Swagger
Location: src/core/decorators/api-standard-responses.decorator.ts
The implementation uses nestjs-cls which leverages Node.js's built-in AsyncLocalStorage to provide type-safe, request-scoped context management.
- Production-Proven Pattern: Used by major companies in production
- Type-Safe Context Access: No type casting needed
- Works with Async/Await: Automatically propagates context through async operations
- Clean Architecture: No need to pass request object through every function
- Thread-Safe: Isolated per request, handles concurrent requests safely
Implementation: 48 bits (12 hex characters) using crypto.randomBytes(6)
Format: req_ prefix + 12 hex characters (e.g., req_88229911aabb)
Why 48 bits?
- Collision Resistance: ~281 trillion possibilities
- High-Traffic Ready: Can handle millions of requests
- Still Readable: Short enough to communicate to support
- Performance: Direct byte generation is fast
Components:
- RequestContextModule (
src/core/context/request-context.module.ts): Global module that initializes CLS - RequestContextService (
src/core/context/request-context.service.ts): Type-safe service for accessing context - RequestIdMiddleware (
src/core/middleware/request-id.middleware.ts): Generates request ID and stores context - RequestContext Interface (
src/core/context/request-context.interface.ts): Type-safe interface for context data
1. Request arrives
↓
2. RequestIdMiddleware
- Generates request ID (48 bits)
- Creates RequestContext object
- Stores in AsyncLocalStorage via ClsService
↓
3. Controller/Service/Interceptor/Filter
- Access context via RequestContextService
- Type-safe, no casting needed
↓
4. Response sent with request_id in body and X-Request-ID header
@Injectable()
export class ProjectsService {
constructor(
private readonly projectsRepository: Repository<Project>,
private readonly requestContext: RequestContextService,
) {}
async findAll() {
const requestId = this.requestContext.getRequestId();
this.logger.log(`[${requestId}] Finding all projects`);
// ... business logic
}
}@Injectable()
export class ResponseTransformInterceptor {
constructor(private readonly requestContext: RequestContextService) {}
intercept(context: ExecutionContext, next: CallHandler) {
const requestId = this.requestContext.getRequestId(); // Type-safe!
// ... transform response
}
}- Type Safety: No
(req as any).requestIdcasting - Clean Code: No need to pass request object everywhere
- Testability: Easy to mock RequestContextService
- Production-Ready: Used by major companies
- Maintainability: Single source of truth for context
- ✅ Response standardization with DTOs (
SuccessResponseDto,ErrorResponseDto) - ✅ Response transformation interceptor
- ✅ Global exception filter with custom exceptions
- ✅ Request ID middleware using AsyncLocalStorage
- ✅ Swagger standardized decorators
- ✅ Pagination utility (
PaginationUtil) - ✅ Base CRUD service (
BaseCrudService) - ✅ Base paginated response DTO (
PaginatedResponseDto) - ✅ Request context service with type-safe access
src/core/interceptors/response-transform.interceptor.ts- Response transformationsrc/core/exceptions/global-exception.filter.ts- Error handlingsrc/core/middleware/request-id.middleware.ts- Request ID generationsrc/core/context/request-context.service.ts- Context accesssrc/core/utils/pagination.util.ts- Pagination logicsrc/core/services/base-crud.service.ts- Base CRUD operationssrc/core/dto/paginated-response.dto.ts- Paginated response base classsrc/core/decorators/api-standard-responses.decorator.ts- Swagger decorators
Last Updated: 2026-01-08