Swagger Package

The @hazeljs/swagger package provides automatic OpenAPI/Swagger documentation generation for your HazelJS applications. It scans your controllers and generates comprehensive API documentation with minimal configuration.

Purpose

API documentation is essential for developer adoption, but maintaining it manually is time-consuming and error-prone. Keeping documentation in sync with code changes requires constant updates. The @hazeljs/swagger package solves this by providing:

  • Automatic Generation: Scans your controllers and generates OpenAPI specs automatically
  • Decorator-Based: Use decorators to document endpoints without writing separate docs
  • Type Safety: Leverages TypeScript types for accurate documentation
  • Interactive UI: Built-in Swagger UI for testing and exploring your API
  • Standards Compliant: Generates OpenAPI 3.0 compliant specifications

Architecture

The package uses reflection and decorators to extract API information:

Loading diagram...

Key Components

  1. SwaggerService: Generates OpenAPI specifications from controllers
  2. SwaggerController: Serves Swagger UI and API specs
  3. Decorators: @Swagger, @ApiOperation for documenting endpoints
  4. Metadata Extraction: Uses reflection to extract route information

Advantages

1. Always Up-to-Date

Documentation is generated from your code, so it's always synchronized with your API.

2. Developer Experience

Use decorators to document endpoints—no need to maintain separate documentation files.

3. Interactive Testing

Swagger UI allows developers to test your API directly from the documentation.

4. Standards Compliant

Generates OpenAPI 3.0 specs that work with all standard tools and clients.

5. Type Safety

Leverages TypeScript types to ensure accurate request/response documentation.

6. Easy Integration

Works seamlessly with HazelJS controllers—no additional setup required.

Installation

npm install @hazeljs/swagger

Quick Start

Basic Setup

import { HazelModule } from '@hazeljs/core';
import { SwaggerModule } from '@hazeljs/swagger';
import { AppModule } from './app.module';

@HazelModule({
  imports: [
    SwaggerModule,
  ],
})
export class AppModule {}

// Set root module for Swagger
SwaggerModule.setRootModule(AppModule);

Access Swagger UI

Once configured, access Swagger UI at:

http://localhost:3000/swagger

Swagger Decorator

Decorators and Annotations

The Swagger package provides decorators for automatically generating OpenAPI documentation. These decorators use metadata to extract API information and generate comprehensive documentation.

Understanding Swagger Decorators

Swagger decorators work together to build OpenAPI specifications:

  • Class Decorators: Configure API-level information
  • Method Decorators: Document individual endpoints
  • Metadata Extraction: Framework reads decorator metadata to generate specs

@Swagger Decorator

The @Swagger decorator is a class decorator that configures OpenAPI documentation for a controller.

Purpose: Defines API-level metadata like title, description, version, and tags. This information appears at the top of your Swagger documentation.

How it works:

  • Stores API metadata in class metadata
  • SwaggerService reads this when generating the OpenAPI spec
  • Information appears in the Swagger UI header and info section

Configuration Options:

interface SwaggerOptions {
  title?: string;              // API title
  description?: string;         // API description
  version?: string;             // API version (e.g., '1.0.0')
  contact?: {                   // Contact information
    name?: string;
    email?: string;
    url?: string;
  };
  license?: {                   // License information
    name: string;
    url?: string;
  };
  servers?: Array<{             // Server URLs
    url: string;
    description?: string;
  }>;
  tags?: Array<{                // API tags for grouping
    name: string;
    description?: string;
  }>;
}

Use the @Swagger decorator to document your controllers:

import { Controller, Get, Post, Body, Param } from '@hazeljs/core';
import { Swagger, ApiOperation } from '@hazeljs/swagger';

// @Swagger is a class decorator that configures API documentation
@Swagger({
  title: 'My API',                    // Appears in Swagger UI header
  description: 'API documentation for my application',
  // Detailed description of what your API does
  // Supports Markdown for formatting
  
  version: '1.0.0',                  // API version
  // Used for versioning and change tracking
  
  tags: [
    { 
      name: 'Users', 
      description: 'User management endpoints' 
    },
    { 
      name: 'Products', 
      description: 'Product management endpoints' 
    },
  ],
  // Tags group related endpoints in Swagger UI
  // Makes documentation easier to navigate
})
@Controller('users')
export class UsersController {
  // All endpoints in this controller will be documented
  // with the above API-level information
  
  @ApiOperation({
    summary: 'Get all users',              // Short description
    // Appears in the endpoint list in Swagger UI
    // Keep it concise (one line)
    
    description: 'Retrieve a list of all users with optional filtering',
    // Detailed description
    // Supports Markdown for formatting
    // Explain what the endpoint does, any side effects, etc.
    
    tags: ['Users'],                      // Group this endpoint
    // Appears under "Users" tag in Swagger UI
    // Can override controller-level tags
    
    responses: {
      '200': {
        description: 'List of users retrieved successfully',
        content: {
          'application/json': {
            schema: {
              type: 'array',
              items: { $ref: '#/components/schemas/User' },
              // Reference to a schema definition
              // Or inline schema definition
            },
          },
        },
      },
    },
    // Documents possible responses
    // Swagger UI shows example responses
    // Helps API consumers understand what to expect
  })
  @Get()
  async findAll() {
    return [];
  }

  // @ApiOperation documents individual endpoints
  @ApiOperation({
    summary: 'Get user by ID',              // Short, one-line summary
    description: 'Retrieve a specific user by their ID. Returns 404 if user not found.',
    // Detailed description with important notes
    
    tags: ['Users'],                        // Group under Users tag
    
    parameters: [
      {
        name: 'id',
        in: 'path',                         // Path parameter (from URL)
        required: true,                     // Must be provided
        schema: { 
          type: 'integer',
          minimum: 1,                       // Validation constraints
        },
        description: 'Unique user identifier',
        example: 123,                       // Example value for Swagger UI
      },
    ],
    // Documents path, query, and header parameters
    // Swagger UI generates input fields based on this
    
    responses: {
      '200': {
        description: 'User found and returned successfully',
        content: {
          'application/json': {
            schema: { $ref: '#/components/schemas/User' },
            // Reference to shared schema
            // Or define inline: schema: { type: 'object', properties: {...} }
          },
        },
      },
      '404': {
        description: 'User with the specified ID was not found',
        // Document all possible error responses
        // Helps API consumers handle errors properly
      },
    },
  })
  @Get(':id')
  async findOne(@Param('id') id: string) {
    // The decorator metadata is used to generate OpenAPI spec
    // Framework extracts route info and combines with decorator metadata
    return { id: parseInt(id) };
  }

  @ApiOperation({
    summary: 'Create a new user',
    description: 'Creates a new user account with the provided information. Email must be unique.',
    tags: ['Users'],
    requestBody: {
      required: true,
      description: 'User creation data',
      content: {
        'application/json': {
          // Inline schema definition with examples
          schema: {
            type: 'object',
            properties: {
              name: { 
                type: 'string',
                example: 'John Doe',
                minLength: 1,
                maxLength: 100,
              },
              email: { 
                type: 'string',
                format: 'email',
                example: 'john@example.com',
              },
            },
            required: ['name', 'email'],
          },
          // Or use schema reference:
          // schema: { $ref: '#/components/schemas/CreateUserDto' }
        },
      },
    },
    responses: {
      '201': {
        description: 'User created successfully',
        content: {
          'application/json': {
            schema: { $ref: '#/components/schemas/User' },
          },
        },
      },
      '400': {
        description: 'Invalid input data. Check validation errors.',
      },
      '409': {
        description: 'User with this email already exists',
      },
    },
  })
  @Post()
  async create(@Body() createUserDto: CreateUserDto) {
    return createUserDto;
  }
}

@ApiOperation Decorator Explained

The @ApiOperation decorator is a method decorator that provides comprehensive documentation for API endpoints.

How it works:

  1. Metadata Storage: Stores operation details in method metadata
  2. Spec Generation: SwaggerService reads this metadata when generating OpenAPI spec
  3. UI Rendering: Swagger UI uses this to render interactive documentation
  4. Route Integration: Combines with route metadata from @Get, @Post, etc.

Key Configuration Areas:

1. Summary and Description:

  • summary: Short, one-line description (appears in endpoint list)
  • description: Detailed explanation (supports Markdown)

2. Parameters: Document path, query, and header parameters:

parameters: [
  {
    name: 'id',
    in: 'path',              // 'path', 'query', or 'header'
    required: true,
    schema: { 
      type: 'integer',
      minimum: 1,
    },
    description: 'User identifier',
    example: 123,
  },
]

3. Request Body: Define request body structure:

requestBody: {
  required: true,
  content: {
    'application/json': {
      schema: {
        type: 'object',
        properties: { /* ... */ },
        required: [/* ... */],
      },
    },
  },
}

4. Responses: Document all possible responses:

responses: {
  '200': { /* success response */ },
  '400': { /* validation error */ },
  '404': { /* not found */ },
  '500': { /* server error */ },
}

Best Practices:

  1. Be comprehensive: Document all parameters, request body, and responses
  2. Use examples: Provide example values for better understanding
  3. Include validation: Add constraints (min, max, pattern) to schemas
  4. Document errors: Include all possible error responses
  5. Use schema references: Reuse common schemas for consistency
  6. Keep it updated: Update documentation when endpoints change

Swagger Service

Use the SwaggerService to programmatically generate OpenAPI specs:

import { Injectable } from '@hazeljs/core';
import { SwaggerService } from '@hazeljs/swagger';

@Injectable()
export class ApiService {
  constructor(private readonly swaggerService: SwaggerService) {}

  getOpenApiSpec() {
    const controllers = [UsersController, ProductsController];
    return this.swaggerService.generateSpec(controllers);
  }
}

Swagger Controller

The SwaggerController provides endpoints for accessing Swagger documentation:

  • GET /swagger - Swagger UI
  • GET /swagger/json - OpenAPI JSON specification
  • GET /swagger/yaml - OpenAPI YAML specification

Complete Example

import { HazelModule } from '@hazeljs/core';
import { SwaggerModule, Swagger, ApiOperation } from '@hazeljs/swagger';
import { Controller, Get, Post, Body, Param } from '@hazeljs/core';

// Define DTOs
interface User {
  id: number;
  name: string;
  email: string;
}

interface CreateUserDto {
  name: string;
  email: string;
}

// Controller with Swagger documentation
@Swagger({
  title: 'User API',
  description: 'User management API',
  version: '1.0.0',
  tags: [{ name: 'Users', description: 'User operations' }],
})
@Controller('users')
export class UsersController {
  @ApiOperation({
    summary: 'List all users',
    tags: ['Users'],
    responses: {
      '200': {
        description: 'Successful response',
      },
    },
  })
  @Get()
  async findAll(): Promise<User[]> {
    return [];
  }

  @ApiOperation({
    summary: 'Get user by ID',
    tags: ['Users'],
    parameters: [
      {
        name: 'id',
        in: 'path',
        required: true,
        schema: { type: 'integer' },
      },
    ],
    responses: {
      '200': { description: 'User found' },
      '404': { description: 'User not found' },
    },
  })
  @Get(':id')
  async findOne(@Param('id') id: string): Promise<User> {
    return { id: parseInt(id), name: 'John', email: 'john@example.com' };
  }

  @ApiOperation({
    summary: 'Create user',
    tags: ['Users'],
    requestBody: {
      required: true,
      content: {
        'application/json': {
          schema: {
            type: 'object',
            properties: {
              name: { type: 'string' },
              email: { type: 'string', format: 'email' },
            },
            required: ['name', 'email'],
          },
        },
      },
    },
    responses: {
      '201': { description: 'User created' },
      '400': { description: 'Invalid input' },
    },
  })
  @Post()
  async create(@Body() createUserDto: CreateUserDto): Promise<User> {
    return {
      id: 1,
      ...createUserDto,
    };
  }
}

// Module setup
@HazelModule({
  imports: [SwaggerModule],
  controllers: [UsersController],
})
export class AppModule {}

SwaggerModule.setRootModule(AppModule);

Customization

Custom Swagger UI Path

Configure a custom path for Swagger UI:

SwaggerModule.forRoot({
  path: '/api-docs', // Custom path
  title: 'My API',
  version: '1.0.0',
});

Additional Configuration

@Swagger({
  title: 'My API',
  description: 'Comprehensive API documentation',
  version: '1.0.0',
  contact: {
    name: 'API Support',
    email: 'support@example.com',
  },
  license: {
    name: 'MIT',
    url: 'https://opensource.org/licenses/MIT',
  },
  servers: [
    { url: 'https://api.example.com', description: 'Production' },
    { url: 'https://staging-api.example.com', description: 'Staging' },
  ],
  tags: [
    { name: 'Users', description: 'User management' },
    { name: 'Products', description: 'Product management' },
  ],
})

Best Practices

  1. Document all endpoints: Use @ApiOperation for every route handler.

  2. Define schemas: Use schema references for request/response bodies.

  3. Add descriptions: Provide clear descriptions for all operations.

  4. Use tags: Organize endpoints with tags for better navigation.

  5. Include examples: Add examples to help API consumers.

  6. Version your API: Include version information in your Swagger configuration.

What's Next?

  • Learn about Controllers for routing
  • Explore Config for API configuration
  • Check out Auth for securing your API