Skip to main content

API Documentation and OpenAPI

Overview

The Wrkbelt API uses OpenAPI (Swagger) to provide comprehensive, interactive API documentation. This documentation is automatically generated from code annotations, ensuring it stays in sync with the actual implementation. This pattern explains how we implement and maintain our API documentation.

Note: For a comprehensive reference of all available Swagger decorators, see the Swagger Decorator Reference table at the end of this document.

Table of Contents

  1. OpenAPI Integration
  2. Controller Documentation
  3. DTO Documentation
  4. Response Documentation
  5. Authentication Documentation
  6. Swagger UI Configuration
  7. Best Practices
  8. Implementation Examples
  9. Swagger Decorator Reference

OpenAPI Integration

Setting Up Swagger

The Swagger configuration is initialized in the application bootstrap process:

// apps/api/src/main.ts

// Swagger setup
const config = new DocumentBuilder()
.setTitle(swaggerConfig.title)
.setDescription(swaggerConfig.description)
.setVersion(swaggerConfig.version)
.addTag(swaggerConfig.tags[0])
.addCookieAuth(sessionConfig.cookieId)
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document, {
swaggerOptions: {
withCredentials: true,
},
});

This configuration:

  1. Sets the API title, description, and version from configuration
  2. Adds tags for organizing endpoints
  3. Configures cookie-based authentication
  4. Creates a Swagger document and makes it available at the /api endpoint

Controller Documentation

Controllers use a combination of NestJS decorators for defining routes and Swagger decorators for documenting them. Let's look at how these work together:

@ApiTags(capitalizeFirstCharacter(API_RESOURCES.PERMISSIONS)) // Swagger decorator
@ApiCookieAuth() // Swagger decorator
@Controller(API_RESOURCES.PERMISSIONS) // NestJS decorator
export class PermissionController {
constructor(private readonly permissionService: PermissionService) {}

@Get() // NestJS decorator
@RequirePermissions(AllPermissions.VIEW_PERMISSIONS) // Custom NestJS decorator
@ApiOperation({ summary: 'Retrieve all permissions' }) // Swagger decorator
@ApiResponse({ status: 200, type: [PermissionEntity] }) // Swagger decorator
async findAll() {
return this.permissionService.findAll();
}

@Post() // NestJS decorator
@RequirePermissions(AllPermissions.CREATE_PERMISSION) // Custom NestJS decorator
@ApiOperation({ summary: 'Create a new permission' }) // Swagger decorator
@ApiBody({ type: CreatePermissionDto }) // Swagger decorator
@ApiCreatedResponse({ type: PermissionEntity }) // Swagger decorator
async create(@Body() createPermissionDto: CreatePermissionDto) {
return this.permissionService.create(createPermissionDto);
}
}
  • @Controller: Maps the controller to a base route
  • @Get, @Post, @Put, @Delete, etc.: Define HTTP methods for routes
  • @Body, @Param, @Query, etc.: Extract data from the request
  • @RequirePermissions: Custom decorator for authorization (not Swagger-related)

Swagger Decorators for Controllers

  • @ApiTags: Organizes endpoints into logical groups in the Swagger UI
  • @ApiCookieAuth: Indicates that the endpoints require cookie-based authentication
  • @ApiOperation: Describes what an endpoint does with a summary and description
  • @ApiResponse/@ApiOkResponse/@ApiCreatedResponse: Document possible responses
  • @ApiBody: Specifies the request body schema for POST/PUT requests
  • @ApiParam: Documents path parameters (e.g., :id)
  • @ApiQuery: Documents query parameters
  • @ApiHeader: Documents required or available headers
  • @ApiProduces: Specifies the MIME types an operation can produce
  • @ApiConsumes: Specifies the MIME types an operation can consume
  • @ApiExcludeEndpoint: Excludes an endpoint from the Swagger documentation
  • @ApiExtraModels: Registers extra models for documentation
  • @ApiBearerAuth: Indicates that an endpoint uses Bearer token authentication

Examples of Specific Swagger Decorators

@ApiOperation

@ApiOperation({
summary: 'Create a new user',
description: 'Creates a new user with the provided data',
})

@ApiParam

@ApiParam({
name: 'id',
description: 'The ID of the resource',
type: 'string',
example: '507f1f77bcf86cd799439011',
})

@ApiQuery

@ApiQuery({
name: 'page',
description: 'Page number for pagination',
type: 'number',
required: false,
example: 1,
})

@ApiBody

@ApiBody({
type: CreateUserDto,
description: 'User data for creation',
examples: {
example1: {
value: {
email: 'user@example.com',
first_name: 'John',
last_name: 'Doe',
},
},
},
})

DTO Documentation

Data Transfer Objects (DTOs) use two types of decorators:

  1. Swagger decorators - For API documentation
  2. Validation decorators - For input validation (from class-validator)

Here's an example showing both in use:

export class CreatePermissionDto implements CreatePermissionDtoBase {
// Swagger decorator for documentation
@ApiProperty({
example: AllPermissions.CREATE_MEMBERSHIP,
description: 'The name of the permission',
uniqueItems: true,
})
// Validation decorators (from class-validator)
@IsNotEmpty()
@IsEnum(AllPermissions)
name!: AllPermissions;

// Swagger decorator for documentation
@ApiProperty({
example:
AllPermissionsMetadata[AllPermissions.CREATE_MEMBERSHIP].description,
description: 'The description of the permission',
})
// Validation decorators (from class-validator)
@IsNotEmpty()
@IsString()
description!: string;
}

Swagger Decorators for DTOs

  • @ApiProperty: Defines a property in the schema

    • example: Provides example values that appear in Swagger UI
    • description: Explains the purpose of each field
    • required: Marks if a property is required (defaults to true)
    • type: Explicitly specifies the type
    • enum: Lists available enum values
    • default: Specifies default value
    • format: Specifies format for numbers or strings (e.g., date-time, email)
    • minimum/maximum: Constraints for number properties
    • minLength/maxLength: Constraints for string properties
    • pattern: Regex pattern for string validation
    • uniqueItems: For arrays, requires items to be unique
  • @ApiPropertyOptional: Shorthand for @ApiProperty({ required: false })

Validation Decorators (class-validator)

These aren't Swagger-specific but affect the generated documentation:

  • @IsNotEmpty: Ensures a value isn't empty
  • @IsEnum: Validates value against an enum
  • @IsString: Validates value is a string
  • @IsNumber: Validates value is a number
  • @IsArray: Validates value is an array
  • @IsBoolean: Validates value is a boolean
  • @IsEmail: Validates value is a valid email
  • @MinLength/@MaxLength: Validates string length
  • @Min/@Max: Validates numeric constraints
  • @Matches: Validates against regex pattern
  • @IsOptional: Makes the validation optional
  • @ValidateNested: Validates nested objects
  • @IsMongoId: Validates MongoDB ObjectIds

These validation rules are automatically reflected in the generated OpenAPI documentation, complementing the explicit Swagger decorators.

Response Documentation

Swagger provides specific decorators to document API responses. These decorators provide detailed information in the OpenAPI specification about what responses an endpoint can return.

Swagger Response Decorators

@Post() // NestJS decorator
@RequirePermissions(AllPermissions.CREATE_PERMISSION) // Custom NestJS decorator
@ApiOperation({ summary: 'Create a new permission' }) // Swagger decorator
@ApiResponse({ // Swagger decorator
status: 201,
description: 'The permission has been successfully created.',
type: PermissionEntity
})
@ApiResponse({ // Swagger decorator
status: 400,
description: 'Invalid input data.'
})
async create(@Body() createPermissionDto: CreatePermissionDto) {
return this.permissionService.create(createPermissionDto);
}

Status-Specific Response Decorators

Swagger provides shortcuts for common HTTP status codes:

@Get() // NestJS decorator
@RequirePermissions(AllPermissions.VIEW_PERMISSIONS) // Custom NestJS decorator
@ApiOperation({ summary: 'List all permissions' }) // Swagger decorator
@ApiOkResponse({ // Swagger decorator - shorthand for status 200
description: 'List of all permissions',
type: [PermissionEntity],
})
@ApiNotFoundResponse({ // Swagger decorator - shorthand for status 404
description: 'No permissions found',
})
@ApiBadRequestResponse({ // Swagger decorator - shorthand for status 400
description: 'Invalid request parameters',
})
@ApiUnauthorizedResponse({ // Swagger decorator - shorthand for status 401
description: 'Not authenticated',
})
@ApiForbiddenResponse({ // Swagger decorator - shorthand for status 403
description: 'Insufficient permissions',
})
async findAll() {
return this.permissionService.findAll();
}

Complete List of Status-Specific Decorators

All of these are Swagger decorators:

  • @ApiOkResponse: Status 200 responses
  • @ApiCreatedResponse: Status 201 responses
  • @ApiAcceptedResponse: Status 202 responses
  • @ApiNoContentResponse: Status 204 responses
  • @ApiMovedPermanentlyResponse: Status 301 responses
  • @ApiBadRequestResponse: Status 400 responses
  • @ApiUnauthorizedResponse: Status 401 responses
  • @ApiNotFoundResponse: Status 404 responses
  • @ApiForbiddenResponse: Status 403 responses
  • @ApiMethodNotAllowedResponse: Status 405 responses
  • @ApiNotAcceptableResponse: Status a406 responses
  • @ApiRequestTimeoutResponse: Status 408 responses
  • @ApiConflictResponse: Status 409 responses
  • @ApiGoneResponse: Status 410 responses
  • @ApiPayloadTooLargeResponse: Status 413 responses
  • @ApiUnsupportedMediaTypeResponse: Status 415 responses
  • @ApiUnprocessableEntityResponse: Status 422 responses
  • @ApiInternalServerErrorResponse: Status 500 responses
  • @ApiNotImplementedResponse: Status 501 responses
  • @ApiBadGatewayResponse: Status 502 responses
  • @ApiServiceUnavailableResponse: Status 503 responses
  • @ApiGatewayTimeoutResponse: Status 504 responses
  • @ApiDefaultResponse: Default responses

Response Schema Documentation

Document the structure of response bodies using the type property:

@ApiOkResponse({
description: 'User details retrieved successfully',
type: UserEntity,
})

For array responses:

@ApiOkResponse({
description: 'List of users retrieved successfully',
type: [UserEntity],
})

For paginated responses, create a wrapper type:

class PaginatedUsersResponse {
@ApiProperty({ type: [UserEntity] })
data: UserEntity[];

@ApiProperty()
total: number;

@ApiProperty()
page: number;

@ApiProperty()
limit: number;
}

// Then use it in the controller
@ApiOkResponse({
description: 'Paginated list of users',
type: PaginatedUsersResponse,
})

Authentication Documentation

Swagger Authentication Configuration

The OpenAPI specification includes authentication information through the Swagger setup in main.ts:

// Swagger-specific configuration in DocumentBuilder
const config = new DocumentBuilder()
.setTitle(swaggerConfig.title)
.setDescription(swaggerConfig.description)
.setVersion(swaggerConfig.version)
.addTag(swaggerConfig.tags[0])
.addCookieAuth(sessionConfig.cookieId) // Swagger-specific auth config
.build();

This .addCookieAuth() method adds an authentication section to the Swagger UI, describing how to authenticate requests with cookies.

Authentication Decorators

The @ApiCookieAuth() decorator (a Swagger-specific decorator) on controllers indicates which endpoints require cookie-based authentication:

@ApiTags('Users') // Swagger decorator
@ApiCookieAuth() // Swagger decorator for auth
@Controller('users') // NestJS decorator
export class UserController {
// Controller implementation
}

Other authentication-related Swagger decorators include:

  • @ApiBearerAuth(): For JWT/Bearer token authentication
  • @ApiBasicAuth(): For HTTP Basic authentication
  • @ApiOAuth2(): For OAuth2 authentication

Swagger UI Configuration

The Swagger UI itself is configured and mounted with the SwaggerModule.setup() method:

// Swagger-specific UI configuration
SwaggerModule.setup('api', app, document, {
swaggerOptions: {
withCredentials: true, // Include credentials with requests
persistAuthorization: true, // Remember authorization between page refreshes
tagsSorter: 'alpha', // Sort tags alphabetically
operationsSorter: 'alpha', // Sort operations alphabetically
},
});

This makes the documentation available at the /api path and configures the UI with specific options for better usability.

Swagger UI Features

The Swagger UI provides several features to help API consumers:

  • Interactive documentation: Try out API endpoints directly from the browser
  • Authentication flow: Use the "Authorize" button to authenticate
  • Schema models: View all DTO and entity schemas
  • Request/response examples: See example payloads for each endpoint
  • Response codes: View documented response codes and descriptions
  • Content negotiation: Test different content types

Customizing Swagger UI

You can customize the Swagger UI appearance and behavior with additional options:

SwaggerModule.setup('api', app, document, {
customSiteTitle: 'Wrkbelt API Documentation', // Sets browser tab title
customCss: '.swagger-ui .topbar { display: none }', // Custom CSS
swaggerOptions: {
docExpansion: 'none', // Collapses operations by default
filter: true, // Enables filtering
showExtensions: true, // Shows vendor extensions
showCommonExtensions: true, // Shows common extensions
},
});

Best Practices

1. Swagger-Specific Documentation Standards

  • Use @ApiTags consistently: Group endpoints logically by resource

    // Swagger decorator - DO THIS
    @ApiTags(capitalizeFirstCharacter(API_RESOURCES.PERMISSIONS))
  • Be specific with @ApiOperation: Add clear summary and description

    // Swagger decorator - DO THIS
    @ApiOperation({
    summary: 'Create a new user',
    description: 'Creates a user account with the provided information.',
    })
  • Document all responses: Include both success and error responses

    // Swagger decorators - DO THIS
    @ApiCreatedResponse({ type: UserEntity })
    @ApiBadRequestResponse({ description: 'Invalid input' })
    @ApiConflictResponse({ description: 'Email already exists' })
  • Provide rich examples: Make examples realistic and comprehensive

    // Swagger decorator in DTO - DO THIS
    @ApiProperty({
    example: 'jane.doe@example.com',
    description: 'User email address',
    })

2. DTO Documentation Best Practices

  • Document every field with @ApiProperty: Don't leave any field undocumented

    // Swagger decorator - DO THIS
    @ApiProperty({
    description: 'The user's first name',
    example: 'Jane',
    minLength: 1,
    maxLength: 50,
    })
    // NON-Swagger decorators for validation
    @IsString()
    @IsNotEmpty()
    @MaxLength(50)
    firstName: string;
  • Align validation rules with documentation: Ensure consistency between class-validator decorators and Swagger property options

  • Use enum documentation: Document enum values properly

    // Swagger decorator - DO THIS
    @ApiProperty({
    enum: UserRole,
    enumName: 'UserRole',
    description: 'The user's role in the system',
    })

3. Response Documentation Best Practices

  • Use specific response decorators: Choose the right decorator for each status code

    // Swagger-specific decorators - DO THIS
    @ApiOkResponse({ type: UserEntity })
    // Instead of
    @ApiResponse({ status: 200, type: UserEntity })
  • Document paginated responses: Create wrapper types for pagination

    // Swagger-decorated class - DO THIS
    class PaginatedResponse<T> {
    @ApiProperty({ isArray: true })
    data: T[];

    @ApiProperty()
    total: number;

    @ApiProperty()
    page: number;
    }

4. Security Documentation Best Practices

  • Mark authentication requirements: Use the appropriate auth decorator

    // Swagger decorator - DO THIS
    @ApiCookieAuth() // For cookie-based auth
    // Or
    @ApiBearerAuth() // For JWT/token auth
  • Document permission requirements: Make clear what permissions are needed

    // NestJS custom decorator - DO THIS
    @RequirePermissions(AllPermissions.VIEW_USERS)
    // AND document it in the operation description
    @ApiOperation({
    summary: 'List all users',
    description: 'Returns a list of all users. Requires VIEW_USERS permission.',
    })

5. Maintaining Documentation Quality

  • Validate Swagger documentation: Run npm run swagger-spec to validate
  • Review documentation in pull requests: Check documentation quality during code review
  • Keep examples up to date: Update example values when entities change
  • Test API documentation: Verify changes in the Swagger UI

Implementation Examples

Complete Controller Example with Swagger Annotations

// Swagger decorators for controller level
@ApiTags('Users')
@ApiCookieAuth()
// NestJS decorators
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}

// NestJS decorators
@Get()
@RequirePermissions(AllPermissions.VIEW_USERS)
// Swagger decorators for endpoint
@ApiOperation({
summary: 'List all users',
description: 'Returns a paginated list of all users in the system.'
})
@ApiQuery({ name: 'page', type: Number, required: false, example: 1 })
@ApiQuery({ name: 'limit', type: Number, required: false, example: 10 })
@ApiOkResponse({
description: 'List of users retrieved successfully',
type: PaginatedUsersResponse,
})
async findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
) {
return this.userService.findAll({ page, limit });
}

// NestJS decorators
@Get(':id')
@RequirePermissions(AllPermissions.VIEW_USERS)
// Swagger decorators for endpoint
@ApiOperation({ summary: 'Get user by ID' })
@ApiParam({
name: 'id',
description: 'User ID',
example: '507f1f77bcf86cd799439011',
})
@ApiOkResponse({
description: 'User found',
type: UserEntity,
})
@ApiNotFoundResponse({
description: 'User not found',
})
async findOne(@Param('id') id: string) {
return this.userService.findOneById(id);
}

// NestJS decorators
@Post()
@RequirePermissions(AllPermissions.CREATE_USER)
// Swagger decorators for endpoint
@ApiOperation({ summary: 'Create a new user' })
@ApiBody({ type: CreateUserDto })
@ApiCreatedResponse({
description: 'User created successfully',
type: UserEntity,
})
@ApiBadRequestResponse({
description: 'Invalid input data',
})
async create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
}

DTO Example with Nested Objects and Mixed Decorators

export class CreateOrganizationDto {
// Swagger decorator for documentation
@ApiProperty({
description: 'Organization name',
example: 'Acme Corporation',
minLength: 2,
maxLength: 100,
})
// Validation decorators (not Swagger-related)
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
name!: string;

// Swagger decorator for documentation
@ApiProperty({
description: 'Organization settings',
example: {
timeZone: 'America/New_York',
language: 'en-US',
},
required: false,
})
// Validation decorators (not Swagger-related)
@IsOptional()
@ValidateNested()
@Type(() => OrganizationSettingsDto)
settings?: OrganizationSettingsDto;
}

Swagger Decorator Reference

This section provides a comprehensive reference of all Swagger decorators available in the @nestjs/swagger package, organized by category.

Controller-Level Decorators

DecoratorPurposeExample
@ApiTagsGroup operations by resource or feature@ApiTags('Users')
@ApiExcludeControllerExclude a controller from documentation@ApiExcludeController()
@ApiCookieAuthMark controller as requiring cookie auth@ApiCookieAuth()
@ApiBearerAuthMark controller as requiring bearer auth@ApiBearerAuth()
@ApiBasicAuthMark controller as requiring basic auth@ApiBasicAuth()
@ApiOAuth2Mark controller as requiring OAuth2 auth@ApiOAuth2(['read', 'write'])
@ApiExtraModelsRegister extra models for documentation@ApiExtraModels(PaginatedResponse)

Operation Decorators

DecoratorPurposeExample
@ApiOperationDocument operation summary and description@ApiOperation({ summary: 'Create user' })
@ApiExcludeEndpointExclude an endpoint from documentation@ApiExcludeEndpoint()
@ApiParamDocument a path parameter@ApiParam({ name: 'id', type: 'string' })
@ApiQueryDocument a query parameter@ApiQuery({ name: 'sort', enum: ['asc', 'desc'] })
@ApiHeaderDocument a header parameter@ApiHeader({ name: 'X-API-Key' })
@ApiBodyDocument request body@ApiBody({ type: CreateUserDto })
@ApiConsumesSpecify media types operation accepts@ApiConsumes('application/json')
@ApiProducesSpecify media types operation returns@ApiProduces('application/json')

Response Decorators

DecoratorPurposeExample
@ApiResponseGeneric response decorator@ApiResponse({ status: 201, type: UserEntity })
@ApiOkResponse200 OK response@ApiOkResponse({ type: UserEntity })
@ApiCreatedResponse201 Created response@ApiCreatedResponse({ type: UserEntity })
@ApiAcceptedResponse202 Accepted response@ApiAcceptedResponse()
@ApiNoContentResponse204 No Content response@ApiNoContentResponse()
@ApiMovedPermanentlyResponse301 Moved Permanently response@ApiMovedPermanentlyResponse()
@ApiBadRequestResponse400 Bad Request response@ApiBadRequestResponse({ description: 'Invalid input' })
@ApiUnauthorizedResponse401 Unauthorized response@ApiUnauthorizedResponse()
@ApiForbiddenResponse403 Forbidden response@ApiForbiddenResponse()
@ApiNotFoundResponse404 Not Found response@ApiNotFoundResponse()
@ApiMethodNotAllowedResponse405 Method Not Allowed response@ApiMethodNotAllowedResponse()
@ApiNotAcceptableResponse406 Not Acceptable response@ApiNotAcceptableResponse()
@ApiRequestTimeoutResponse408 Request Timeout response@ApiRequestTimeoutResponse()
@ApiConflictResponse409 Conflict response@ApiConflictResponse()
@ApiGoneResponse410 Gone response@ApiGoneResponse()
@ApiPayloadTooLargeResponse413 Payload Too Large response@ApiPayloadTooLargeResponse()
@ApiUnsupportedMediaTypeResponse415 Unsupported Media Type response@ApiUnsupportedMediaTypeResponse()
@ApiUnprocessableEntityResponse422 Unprocessable Entity response@ApiUnprocessableEntityResponse()
@ApiInternalServerErrorResponse500 Internal Server Error response@ApiInternalServerErrorResponse()
@ApiNotImplementedResponse501 Not Implemented response@ApiNotImplementedResponse()
@ApiBadGatewayResponse502 Bad Gateway response@ApiBadGatewayResponse()
@ApiServiceUnavailableResponse503 Service Unavailable response@ApiServiceUnavailableResponse()
@ApiGatewayTimeoutResponse504 Gateway Timeout response@ApiGatewayTimeoutResponse()
@ApiDefaultResponseDefault response@ApiDefaultResponse({ description: 'Unexpected error' })

Schema Decorators

DecoratorPurposeExample
@ApiPropertyDocument a model property@ApiProperty({ type: String, example: 'John' })
@ApiPropertyOptionalDocument an optional model property@ApiPropertyOptional({ type: String })
@ApiResponsePropertyDocument a property used only in responses@ApiResponseProperty({ type: Date })
@ApiHidePropertyExclude property from documentation@ApiHideProperty()