API Documentation with HazelJS Swagger Module
Learn how to auto-generate OpenAPI docs and interactive explorers for your HazelJS backend.
Introduction
API documentation is crucial for developer experience and API adoption. The HazelJS Swagger module automatically generates interactive OpenAPI documentation from your controllers, eliminating the need for manual documentation maintenance. With decorator-based annotations, your code becomes self-documenting.
Installation
Install the Swagger package:
npm install @hazeljs/swaggerBasic Setup
Enable Swagger in your application with just a few lines:
import { HazelApp } from '@hazeljs/core';
import { SwaggerModule } from '@hazeljs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = new HazelApp(AppModule);
// Enable Swagger documentation
SwaggerModule.setRootModule(AppModule);
await app.listen(3000);
console.log('📚 API docs available at http://localhost:3000/swagger/');
}
bootstrap();Controller-Level Documentation
Use the @Swagger decorator to document your controllers:
import { Controller, Get, Post } from '@hazeljs/core';
import { Swagger } from '@hazeljs/swagger';
@Controller('/users')
@Swagger({
title: 'User API',
description: 'User management endpoints',
version: '1.0.0',
tags: [
{ name: 'users', description: 'User operations' }
],
})
export class UserController {
// Your endpoints here
}Operation-Level Documentation
Document individual endpoints with the @ApiOperation decorator:
import { Controller, Get, Post, Body, Param } from '@hazeljs/core';
import { Swagger, ApiOperation } from '@hazeljs/swagger';
@Controller('/users')
@Swagger({
title: 'User API',
description: 'User management endpoints',
version: '1.0.0',
tags: [{ name: 'users', description: 'User operations' }],
})
export class UserController {
@Get()
@ApiOperation({
summary: 'Get all users',
description: 'Retrieve a list of all users in the system',
tags: ['users'],
responses: {
'200': {
description: 'List of users',
content: {
'application/json': {
schema: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
},
},
},
},
})
findAll() {
return [{ id: '1', name: 'John', email: 'john@example.com' }];
}
@Get('/:id')
@ApiOperation({
summary: 'Get user by ID',
description: 'Retrieve a specific user by their ID',
tags: ['users'],
parameters: [
{
name: 'id',
in: 'path',
required: true,
description: 'User ID',
schema: { type: 'string' },
},
],
responses: {
'200': {
description: 'User found',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
},
},
'404': {
description: 'User not found',
},
},
})
findOne(@Param('id') id: string) {
return { id, name: 'John', email: 'john@example.com' };
}
@Post()
@ApiOperation({
summary: 'Create a new user',
description: 'Create a new user account',
tags: ['users'],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', example: 'John Doe' },
email: { type: 'string', format: 'email', example: 'john@example.com' },
age: { type: 'integer', minimum: 0, example: 30 },
},
},
},
},
},
responses: {
'201': {
description: 'User created successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
},
},
'400': {
description: 'Invalid input',
},
},
})
create(@Body() createUserDto: CreateUserDto) {
return { id: '1', ...createUserDto };
}
}Complete Example
Here's a complete example showing Swagger integration with a real controller:
import { Controller, Get, Post, Delete, Param, Body } from '@hazeljs/core';
import { Cache } from '@hazeljs/cache';
import { Swagger, ApiOperation } from '@hazeljs/swagger';
@Controller('/products')
@Swagger({
title: 'Product API',
description: 'Product catalog management endpoints',
version: '1.0.0',
tags: [
{ name: 'products', description: 'Product operations' },
{ name: 'catalog', description: 'Catalog management' },
],
})
export class ProductController {
@Get()
@Cache({ strategy: 'memory', ttl: 300 })
@ApiOperation({
summary: 'List all products',
description: 'Get a paginated list of all products',
tags: ['products'],
parameters: [
{
name: 'page',
in: 'query',
schema: { type: 'integer', default: 1 },
description: 'Page number',
},
{
name: 'limit',
in: 'query',
schema: { type: 'integer', default: 10, maximum: 100 },
description: 'Items per page',
},
],
responses: {
'200': {
description: 'List of products',
},
},
})
findAll() {
return { products: [], total: 0 };
}
@Get('/:id')
@Cache({ strategy: 'memory', ttl: 600, key: 'product-{id}' })
@ApiOperation({
summary: 'Get product by ID',
description: 'Retrieve detailed information about a specific product',
tags: ['products'],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string' },
description: 'Product ID',
},
],
responses: {
'200': {
description: 'Product details',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
price: { type: 'number', format: 'float' },
description: { type: 'string' },
inStock: { type: 'boolean' },
},
},
},
},
},
'404': {
description: 'Product not found',
},
},
})
findOne(@Param('id') id: string) {
return {
id,
name: 'Sample Product',
price: 99.99,
description: 'A sample product',
inStock: true,
};
}
@Post()
@ApiOperation({
summary: 'Create a new product',
description: 'Add a new product to the catalog',
tags: ['products', 'catalog'],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['name', 'price'],
properties: {
name: {
type: 'string',
minLength: 1,
maxLength: 255,
example: 'New Product',
},
price: {
type: 'number',
minimum: 0,
example: 49.99,
},
description: {
type: 'string',
example: 'Product description',
},
category: {
type: 'string',
enum: ['electronics', 'clothing', 'books'],
example: 'electronics',
},
},
},
},
},
},
responses: {
'201': {
description: 'Product created successfully',
},
'400': {
description: 'Invalid input data',
},
},
})
create(@Body() createProductDto: CreateProductDto) {
return { id: '1', ...createProductDto };
}
}Accessing the Documentation
Once Swagger is enabled, access your API documentation at:
- Swagger UI:
http://localhost:3000/swagger/- Interactive API explorer - OpenAPI Spec:
http://localhost:3000/swagger/spec- Raw OpenAPI JSON specification
Features
- Automatic Generation: Documentation is automatically generated from your decorators
- Interactive UI: Swagger UI provides a beautiful, interactive interface to test your APIs
- OpenAPI 3.0: Full OpenAPI 3.0 specification support
- Type Safety: TypeScript types ensure your documentation matches your code
- Request/Response Schemas: Define detailed request and response schemas
- Parameters: Document path, query, header, and cookie parameters
- Tags: Organize endpoints into logical groups
- Examples: Include example values for better documentation
Advanced Usage
Multiple Tags
Organize endpoints across multiple tags:
@Controller('/orders')
@Swagger({
title: 'Order API',
description: 'Order management endpoints',
version: '1.0.0',
tags: [
{ name: 'orders', description: 'Order operations' },
{ name: 'payments', description: 'Payment processing' },
],
})
export class OrderController {
@Post('/:id/pay')
@ApiOperation({
summary: 'Process payment',
tags: ['orders', 'payments'], // Appears in both tag groups
})
async processPayment(@Param('id') id: string) {
// Payment logic
}
}Complex Schemas
Define complex nested schemas:
@ApiOperation({
summary: 'Create order with items',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['customerId', 'items'],
properties: {
customerId: { type: 'string' },
items: {
type: 'array',
items: {
type: 'object',
required: ['productId', 'quantity'],
properties: {
productId: { type: 'string' },
quantity: { type: 'integer', minimum: 1 },
price: { type: 'number' },
},
},
},
shippingAddress: {
type: 'object',
properties: {
street: { type: 'string' },
city: { type: 'string' },
zipCode: { type: 'string' },
country: { type: 'string' },
},
},
},
},
},
},
},
})
async createOrder(@Body() dto: CreateOrderDto) {
// Order creation logic
}Query Parameters
Document query parameters:
@Get('/search')
@ApiOperation({
summary: 'Search products',
parameters: [
{
name: 'q',
in: 'query',
required: true,
schema: { type: 'string' },
description: 'Search query',
},
{
name: 'category',
in: 'query',
schema: {
type: 'string',
enum: ['electronics', 'clothing', 'books'],
},
description: 'Filter by category',
},
{
name: 'minPrice',
in: 'query',
schema: { type: 'number', minimum: 0 },
description: 'Minimum price',
},
{
name: 'maxPrice',
in: 'query',
schema: { type: 'number', minimum: 0 },
description: 'Maximum price',
},
],
})
async search(@Query('q') query: string) {
// Search logic
}Best Practices
- Document Everything: Add
@ApiOperationto all public endpoints - Use Descriptive Summaries: Clear summaries help developers understand endpoints quickly
- Include Examples: Examples make it easier for developers to use your API
- Organize with Tags: Use tags to group related endpoints
- Document Responses: Include all possible response codes and their meanings
- Validate Schemas: Ensure your request/response schemas match your DTOs
Integration with Other Features
Swagger works seamlessly with other HazelJS features:
- Validation: Request validation errors are documented in responses
- Caching: Cache decorators don't interfere with documentation
- Authentication: Document protected endpoints with security schemes
- Error Handling: Exception filters and error responses are automatically documented
Conclusion
The HazelJS Swagger module makes API documentation effortless. With decorator-based annotations, your code becomes self-documenting, and your API documentation stays in sync with your implementation automatically. No more outdated documentation—just write your code with decorators, and beautiful, interactive API docs are generated for you.
Start documenting your APIs today and provide an excellent developer experience for your API consumers!