Skip to Content
Response Documentation

Response Documentation

This documentation explains the features and usage of Response Module: Located at src/common/response

Overview

Complete NestJS Boilerplate standardizes API responses through decorators that automatically format responses, handle pagination, manage file downloads, and set custom headers. Each decorator uses an interceptor to transform data into consistent structures with metadata, status codes, and localized messages.

Table of Contents

Response Decorators

@Response

Standard API response decorator with optional caching.

Parameters:

  • messagePath (string): Path to response message for localization
  • options (optional): Configuration options
    • cache (boolean | object): Enable caching

Interceptor: ResponseInterceptor - transforms responses into standard format with metadata and localized messages via MessageService

Usage:

@Response('user.get') @Get('/:id') async getUser(@Param('id') id: string): Promise<IResponseReturn<UserDto>> { return { data: await this.userService.findById(id) }; }

Custom Status Code:

@Response('user.create') @Post('/') async createUser(@Body() dto: CreateUserDto): Promise<IResponseReturn<UserDto>> { try { const data = await this.userService.create(dto); // Response: { statusCode: 201, message: "...", data: {...}, metadata: {...} } return { data, metadata: { statusCode: 201, httpStatus: HttpStatus.CREATED } }; } catch { // Response: { statusCode: 200, message: "...", data: {...}, metadata: {...} } return { data, metadata: { statusCode: 200, httpStatus: HttpStatus.OK } }; } }

Custom Message:

@Response('user.update') @Patch('/:id') async updateUser(@Param('id') id: string, @Body() dto: UpdateUserDto): Promise<IResponseReturn<UserDto>> { const user = await this.userService.update(id, dto); return { data: user, metadata: { messagePath: 'user.updateSuccess', messageProperties: { name: user.name } } }; }

@ResponsePaging

Paginated API response decorator with optional caching. Supports both offset-based and cursor-based pagination.

Parameters:

  • messagePath (string): Path to response message for localization
  • options (optional): Configuration options
    • cache (boolean | object): Enable caching

Requirements:

  • Request must include pagination parameters (see Pagination Documentation)
  • Response must implement IResponsePagingReturn<T> interface
  • Must specify pagination type: 'offset' or 'cursor'

Interceptor: ResponsePagingInterceptor - validates pagination data, supports offset and cursor-based pagination, includes search/filter/sort metadata

Offset-based Pagination:

@ResponsePaging('user.list') @Get('/list') async listUsers( @PaginationQuery() { page, perPage, orderBy, orderDirection }: PaginationListDto ): Promise<IResponsePagingReturn<UserDto>> { const { data, totalPage, count } = await this.userService.findAll({ page, perPage, orderBy, orderDirection }); return { type: 'offset', data, totalPage, page, perPage, count, hasNext: page < totalPage, nextPage: page < totalPage ? page + 1 : undefined, previousPage: page > 1 ? page - 1 : undefined }; }

Cursor-based Pagination:

@ResponsePaging('user.list') @Get('/list') async listUsers( @PaginationQuery() query: PaginationListDto ): Promise<IResponsePagingReturn<UserDto>> { const { data, cursor, count, hasNext } = await this.userService.findAllCursor(query); return { type: 'cursor', data, cursor, perPage: query.perPage, count, hasNext }; }

@ResponseFile

File download response decorator that handles CSV and PDF file downloads with proper headers and streaming.

Parameters: None

Requirements:

  • Response must implement IResponseFileReturn interface (union of IResponseCsvReturn | IResponsePdfReturn)
  • Must specify extension: EnumFileExtensionDocument.csv or EnumFileExtensionDocument.pdf
  • CSV data must be a string (pre-converted to CSV format)
  • PDF data must be a Buffer
  • Optional filename - if not provided, generates timestamped filename: export-{timestamp}.{extension}

Interceptor: ResponseFileInterceptor - validates data based on extension type, converts to Buffer, sets content headers (Content-Type, Content-Disposition, Content-Length), returns StreamableFile

CSV Export (Auto-generated Filename):

@ResponseFile() @Get('/export/csv') async exportUsersCsv(): Promise<IResponseCsvReturn> { const csvData = this.fileService.writeCsv(users); return { data: csvData, extension: EnumFileExtensionDocument.csv // Filename will be: export-{timestamp}.csv }; }

CSV with Custom Filename:

@ResponseFile() @Get('/export/users') async exportUsersCustom(): Promise<IResponseCsvReturn> { const users = await this.userService.findAll(); const csvData = this.fileService.writeCsv(users); return { data: csvData, extension: EnumFileExtensionDocument.csv, filename: 'users-export.csv' }; }

CSV with Formatted Data:

@ResponseFile() @Get('/export/report') async exportUsersReport(): Promise<IResponseCsvReturn> { const users = await this.userService.findAll(); const formattedData = users.map(user => ({ Name: user.name, Email: user.email, 'Created At': new Date(user.createdAt).toLocaleDateString(), Status: user.isActive ? 'Active' : 'Inactive' })); const csvData = this.fileService.writeCsv(formattedData); return { data: csvData, extension: EnumFileExtensionDocument.csv, filename: 'user-report.csv' }; }

PDF Export:

@ResponseFile() @Get('/export/pdf') async exportUsersPdf(): Promise<IResponsePdfReturn> { const users = await this.userService.findAll(); // Generate PDF using external library (e.g., pdfkit, puppeteer, jsPDF) // Example: const pdfBuffer = await generatePdfReport(users); const pdfBuffer = Buffer.from('...'); // Your PDF generation logic here return { data: pdfBuffer, extension: EnumFileExtensionDocument.pdf, filename: 'users-report.pdf' }; }

Dynamic Format Export:

@ResponseFile() @Get('/export') async exportUsers(@Query('format') format: 'csv' | 'pdf'): Promise<IResponseFileReturn> { const users = await this.userService.findAll(); const timestamp = Date.now(); if (format === 'pdf') { // Generate PDF buffer using your preferred PDF library const pdfBuffer = Buffer.from('...'); // Your PDF generation logic return { data: pdfBuffer, extension: EnumFileExtensionDocument.pdf, filename: `users-${timestamp}.pdf` }; } const csvData = this.fileService.writeCsv(users); return { data: csvData, extension: EnumFileExtensionDocument.csv, filename: `users-${timestamp}.csv` }; }

Response Structure

Standard

{ statusCode: number; message: string; metadata: { language: string; timestamp: number; timezone: string; path: string; version: string; repoVersion: string; requestId: string; correlationId: string; }; data?: T; }

Paginated

{ statusCode: number; message: string; metadata: { // Base metadata language: string; timestamp: number; timezone: string; path: string; version: string; repoVersion: string; requestId: string; correlationId: string; // Pagination metadata type: 'offset' | 'cursor'; // Pagination type search?: string; filters?: Record<string, any>; perPage: number; // Offset-specific fields (when type = 'offset') page?: number; totalPage?: number; count?: number; nextPage?: number; previousPage?: number; // Cursor-specific fields (when type = 'cursor') nextCursor?: string; previousCursor?: string; count?: number; // Optional, included if requested // Common fields hasNext: boolean; orderBy: string; orderDirection: 'asc' | 'desc'; availableSearch: string[]; availableOrderBy: string[]; }; data: T[]; }

Activity Log Metadata (Optional)

All response types (IResponseReturn, IResponsePagingReturn, IResponseFileReturn) support optional activity log metadata for request tracking and auditing:

return { data: user, metadataActivityLog: { // Activity log tracking data } };

See Activity Log Documentation for complete implementation details.

Caching

@Response and @ResponsePaging support optional caching via ResponseCacheInterceptor (extends NestJS CacheInterceptor with custom prefixes).

Basic Caching:

@Response('user.get', { cache: true }) @Get('/:id') async getUser(@Param('id') id: string): Promise<IResponseReturn<UserDto>> { return { data: await this.userService.findById(id) }; }

Cache Key:

Apis:*

Custom Cache Configuration:

@Response('user.get', { cache: { key: 'user-detail', ttl: 3600 // seconds } }) @Get('/:id') async getUser(@Param('id') id: string): Promise<IResponseReturn<UserDto>> { return { data: await this.userService.findById(id) }; }

See NestJS Cache Manager  and Cache Documentation for configuration.

Custom Headers

All responses automatically include these headers (set by interceptors):

  • x-custom-lang: Response language
  • x-timestamp: Response timestamp
  • x-timezone: Response timezone
  • x-version: API version
  • x-repo-version: Repository version
  • x-request-id: Unique request identifier
  • x-correlation-id: Request correlation identifier
Last updated on