API Documentation
This guide covers the API documentation practices and standards used in the Awesome NestJS Boilerplate, including Swagger/OpenAPI integration, authentication patterns, and RESTful API design principles.
- API Documentation
OpenAPI (Swagger) Documentation
The API is documented using OpenAPI 3.0 specifications with automatic generation from NestJS decorators and DTOs.
Accessing the Documentation
When the application is running in development mode:
- Swagger UI:
http://localhost:3000/documentation - OpenAPI JSON:
http://localhost:3000/documentation-json - OpenAPI YAML:
http://localhost:3000/documentation-yaml
Swagger Configuration
The Swagger setup is configured in src/setup-swagger.ts:
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import type { INestApplication } from '@nestjs/common';
export function setupSwagger(app: INestApplication): void {
const options = new DocumentBuilder()
.setTitle('Awesome NestJS Boilerplate API')
.setDescription('RESTful API built with NestJS, TypeScript, and PostgreSQL')
.setVersion('11.0.0')
.addBearerAuth()
.addTag('auth', 'Authentication endpoints')
.addTag('users', 'User management endpoints')
.addTag('posts', 'Post management endpoints')
.addTag('health', 'Health check endpoints')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('documentation', app, document, {
swaggerOptions: {
persistAuthorization: true,
},
});
}API Structure
Base URL and Versioning
- Base URL:
http://localhost:3000 - API Versioning: Enabled through NestJS versioning
- Global Prefix: Can be configured (currently no global prefix)
Content Types
- Request Content-Type:
application/json - Response Content-Type:
application/json - Character Encoding: UTF-8
Authentication
JWT Authentication
The API uses JWT (JSON Web Tokens) for stateless authentication:
// Authentication header format
Authorization: Bearer <jwt_token>Token Structure:
{
"sub": "user-uuid",
"email": "user@example.com",
"role": "USER",
"iat": 1640995200,
"exp": 1640998800
}Protected Endpoints
Most endpoints require authentication using the @Auth() decorator:
@Controller('users')
@ApiTags('users')
export class UserController {
@Get(':id')
@Auth([RoleType.USER, RoleType.ADMIN])
@ApiOkResponse({ type: UserDto })
async getUser(@UUIDParam('id') userId: Uuid): Promise<UserDto> {
return this.userService.getUser(userId);
}
}API Endpoints
Authentication Endpoints
POST /auth/login
Login with email and password.
Request Body:
{
"email": "user@example.com",
"password": "password123"
}Response:
{
"user": {
"id": "uuid",
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe",
"role": "USER"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}POST /auth/register
Register a new user account.
Request Body:
{
"firstName": "John",
"lastName": "Doe",
"email": "user@example.com",
"password": "password123"
}User Management
GET /users/:id
Get user by ID (requires authentication).
Parameters:
id(UUID): User identifier
Response:
{
"id": "uuid",
"firstName": "John",
"lastName": "Doe",
"email": "user@example.com",
"role": "USER",
"createdAt": "2023-01-01T00:00:00.000Z",
"updatedAt": "2023-01-01T00:00:00.000Z"
}GET /users
Get paginated list of users (requires ADMIN role).
Query Parameters:
page(number, optional): Page number (default: 1)take(number, optional): Items per page (default: 10)order(string, optional): Sort order (ASC/DESC)
Post Management
POST /posts
Create a new post (requires authentication).
Request Body:
{
"title": [
{
"languageCode": "en",
"text": "Post Title"
}
],
"description": [
{
"languageCode": "en",
"text": "Post description"
}
]
}GET /posts/:id
Get post by ID.
Response:
{
"id": "uuid",
"title": "Post Title",
"description": "Post description",
"translations": [
{
"languageCode": "en",
"title": "Post Title",
"description": "Post description"
}
],
"createdAt": "2023-01-01T00:00:00.000Z",
"updatedAt": "2023-01-01T00:00:00.000Z"
}Health Check
GET /health
Check application health status.
Response:
{
"status": "ok",
"info": {
"database": {
"status": "up"
}
},
"error": {},
"details": {
"database": {
"status": "up"
}
}
}Request/Response Patterns
Standard Response Format
All API responses follow a consistent structure:
Success Response:
{
"data": { /* response data */ },
"meta": { /* metadata (for paginated responses) */ }
}Single Resource:
{
"id": "uuid",
"name": "Resource Name",
"createdAt": "2023-01-01T00:00:00.000Z"
}Pagination
Paginated endpoints use the following structure:
Request Query Parameters:
GET /users?page=1&take=10&order=ASCPaginated Response:
{
"data": [
{
"id": "uuid",
"name": "User 1"
}
],
"meta": {
"page": 1,
"take": 10,
"itemCount": 2,
"pageCount": 1,
"hasPreviousPage": false,
"hasNextPage": false
}
}Error Responses
All error responses follow a consistent format:
{
"statusCode": 400,
"message": "Validation failed",
"error": "Bad Request",
"timestamp": "2023-01-01T00:00:00.000Z",
"path": "/api/users"
}DTOs and Validation
Input DTOs
Input DTOs use custom validation decorators:
export class CreateUserDto {
@StringField({ minLength: 2, maxLength: 50 })
readonly firstName!: string;
@StringField({ minLength: 2, maxLength: 50 })
readonly lastName!: string;
@EmailField()
readonly email!: string;
@StringField({ minLength: 6 })
readonly password!: string;
@PhoneFieldOptional()
readonly phone?: string;
}Response DTOs
Response DTOs extend AbstractDto for consistent structure:
export class UserDto extends AbstractDto {
@StringFieldOptional({ nullable: true })
firstName?: string | null;
@StringFieldOptional({ nullable: true })
lastName?: string | null;
@EmailFieldOptional({ nullable: true })
email?: string | null;
@EnumFieldOptional(() => RoleType)
role?: RoleType;
constructor(user: UserEntity, options?: UserDtoOptions) {
super(user);
this.firstName = user.firstName;
this.lastName = user.lastName;
this.email = user.email;
this.role = user.role;
}
}Validation Decorators
Custom validation decorators provide both validation and Swagger documentation:
// String validation
@StringField({ minLength: 3, maxLength: 50 })
name!: string;
// Email validation
@EmailField()
email!: string;
// Number validation
@NumberField({ min: 0, max: 100 })
age!: number;
// Boolean validation
@BooleanField()
isActive!: boolean;
// UUID validation
@UUIDField()
id!: string;
// Optional fields
@StringFieldOptional()
description?: string;
// Array validation
@StringField({ each: true })
tags!: string[];Error Handling
HTTP Status Codes
The API uses standard HTTP status codes:
- 200 OK: Successful GET, PUT, PATCH requests
- 201 Created: Successful POST requests
- 204 No Content: Successful DELETE requests
- 400 Bad Request: Invalid request data
- 401 Unauthorized: Missing or invalid authentication
- 403 Forbidden: Insufficient permissions
- 404 Not Found: Resource not found
- 422 Unprocessable Entity: Validation errors
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Server errors
Error Response Format
// Validation error (422)
{
"statusCode": 422,
"message": [
{
"property": "email",
"constraints": {
"isEmail": "email must be an email"
}
}
],
"error": "Unprocessable Entity"
}
// Not found error (404)
{
"statusCode": 404,
"message": "User not found",
"error": "Not Found"
}
// Unauthorized error (401)
{
"statusCode": 401,
"message": "Unauthorized",
"error": "Unauthorized"
}Security
CORS Configuration
CORS is configured to allow specific origins:
// main.ts
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
cors: {
origin: process.env.CORS_ORIGINS?.split(',') || ['http://localhost:3000'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
credentials: true,
}
});Rate Limiting
Rate limiting is implemented using @nestjs/throttler:
// Default configuration
{
ttl: 60, // Time window in seconds
limit: 10 // Maximum requests per time window
}Input Validation
All input is validated using:
- Class-validator: DTO validation
- Class-transformer: Data transformation
- Custom validators: Business logic validation
Internationalization
The API supports multiple languages through the Accept-Language header or lang query parameter:
GET /posts
Accept-Language: en-US
# or
GET /posts?lang=enSupported languages:
en(English)ru(Russian)
API Testing
Using Swagger UI
- Navigate to
http://localhost:3000/documentation - Click "Authorize" and enter your JWT token
- Test endpoints directly from the interface
Using cURL
# Login
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"password123"}'
# Get user (with token)
curl -X GET http://localhost:3000/users/uuid \
-H "Authorization: Bearer your-jwt-token"
# Create post
curl -X POST http://localhost:3000/posts \
-H "Authorization: Bearer your-jwt-token" \
-H "Content-Type: application/json" \
-d '{
"title": [{"languageCode": "en", "text": "My Post"}],
"description": [{"languageCode": "en", "text": "Post description"}]
}'Using Postman
- Import the OpenAPI specification from
http://localhost:3000/documentation-json - Set up environment variables for base URL and authentication token
- Use the pre-configured requests
Best Practices
API Design
- RESTful URLs: Use nouns for resources, verbs for actions
- HTTP Methods: Use appropriate HTTP methods (GET, POST, PUT, DELETE)
- Status Codes: Return meaningful HTTP status codes
- Versioning: Plan for API versioning from the start
Documentation
- Swagger Decorators: Use comprehensive Swagger decorators
- Examples: Provide request/response examples
- Descriptions: Write clear endpoint descriptions
- Tags: Group related endpoints with tags
- Required @ApiOperation: ALWAYS include
@ApiOperation()decorator with meaningful description for every controller endpoint
Swagger @ApiOperation Requirement
Every controller endpoint MUST include an @ApiOperation() decorator with both summary and description properties. These descriptions will be used for semantic search functionality in the future.
✅ Required Format:
@Controller('users')
@ApiTags('users')
export class UserController {
@Get(':id')
@Auth([RoleType.USER])
@ApiOperation({
summary: 'Get user by ID',
description: 'Retrieves a specific user by their unique identifier. Requires user authentication and returns complete user profile data.'
})
@ApiOkResponse({ type: UserDto })
async getUser(@UUIDParam('id') userId: Uuid): Promise<UserDto> {
return this.userService.getUser(userId);
}
@Post()
@Auth([RoleType.ADMIN])
@ApiOperation({
summary: 'Create new user',
description: 'Creates a new user account with the provided data. Validates input, checks for duplicates, and returns the created user with generated ID.'
})
@ApiCreatedResponse({ type: UserDto })
async createUser(@Body() createUserDto: CreateUserDto): Promise<UserDto> {
return this.userService.createUser(createUserDto);
}
}❌ Incorrect - Missing @ApiOperation:
@Controller('users')
@ApiTags('users')
export class UserController {
@Get(':id')
@Auth([RoleType.USER])
@ApiOkResponse({ type: UserDto })
async getUser(@UUIDParam('id') userId: Uuid): Promise<UserDto> {
return this.userService.getUser(userId);
}
}Description Guidelines:
- Start with an action verb (Get, Create, Update, Delete, etc.)
- Explain what the endpoint does and what it returns
- Mention authentication/authorization requirements
- Include relevant business logic context
- Keep descriptions concise but informative (1-2 sentences)
- Use present tense and active voice
Security
- Authentication: Require authentication for sensitive endpoints
- Authorization: Implement role-based access control
- Validation: Validate all input data
- Rate Limiting: Implement rate limiting to prevent abuse
Performance
- Pagination: Implement pagination for list endpoints
- Filtering: Allow filtering and sorting of results
- Caching: Implement appropriate caching strategies
- Compression: Enable response compression
Error Handling
- Consistent Format: Use consistent error response format
- Meaningful Messages: Provide clear error messages
- Logging: Log errors for debugging
- Graceful Degradation: Handle errors gracefully
Testing
- Unit Tests: Test individual components
- Integration Tests: Test API endpoints
- E2E Tests: Test complete user workflows
- Documentation Tests: Ensure documentation matches implementation