+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 213 of 354

๐Ÿ“˜ API Documentation: Swagger/OpenAPI

Master api documentation: swagger/openapi in TypeScript with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Basic understanding of JavaScript ๐Ÿ“
  • TypeScript installation โšก
  • VS Code or preferred IDE ๐Ÿ’ป

What you'll learn

  • Understand the concept fundamentals ๐ŸŽฏ
  • Apply the concept in real projects ๐Ÿ—๏ธ
  • Debug common issues ๐Ÿ›
  • Write type-safe code โœจ

๐ŸŽฏ Introduction

Welcome to this exciting tutorial on API Documentation with Swagger/OpenAPI! ๐ŸŽ‰ In this guide, weโ€™ll explore how to document your TypeScript APIs professionally and make them a joy to use.

Youโ€™ll discover how Swagger/OpenAPI can transform your API development experience. Whether youโ€™re building REST APIs ๐ŸŒ, server-side applications ๐Ÿ–ฅ๏ธ, or microservices ๐Ÿ“š, understanding API documentation is essential for creating robust, maintainable, and user-friendly APIs.

By the end of this tutorial, youโ€™ll feel confident documenting your TypeScript APIs like a pro! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding API Documentation

๐Ÿค” What is Swagger/OpenAPI?

Swagger/OpenAPI is like a detailed blueprint for your house ๐Ÿ . Think of it as a comprehensive manual that tells everyone exactly how to interact with your API - what rooms (endpoints) exist, what furniture (parameters) they contain, and what you can do in each room (operations).

In TypeScript terms, OpenAPI provides a standardized way to describe your API endpoints, request/response models, and authentication methods ๐Ÿ“‹. This means you can:

  • โœจ Generate interactive documentation automatically
  • ๐Ÿš€ Create client SDKs in multiple languages
  • ๐Ÿ›ก๏ธ Validate requests and responses
  • ๐Ÿ”ง Mock your API for testing

๐Ÿ’ก Why Use Swagger/OpenAPI?

Hereโ€™s why API developers love Swagger/OpenAPI:

  1. Self-Documenting APIs ๐Ÿ“–: Your code becomes the documentation
  2. Interactive Testing ๐ŸŽฎ: Test endpoints directly in the browser
  3. Code Generation ๐Ÿค–: Auto-generate client libraries
  4. Standardization ๐Ÿ“: Follow industry-standard specification
  5. Team Collaboration ๐Ÿค: Frontend and backend teams stay in sync

Real-world example: Imagine building a pizza ordering API ๐Ÿ•. With OpenAPI, you can describe exactly how to order a pizza, what toppings are available, and what the response looks like - all in a format thatโ€™s both human and machine readable!

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Simple OpenAPI Specification

Letโ€™s start with a friendly example:

// ๐Ÿ‘‹ Hello, OpenAPI!
import { OpenAPIV3 } from 'openapi-types';

// ๐ŸŽจ Basic API specification
const apiSpec: OpenAPIV3.Document = {
  openapi: '3.0.0',
  info: {
    title: 'Pizza Palace API ๐Ÿ•',
    version: '1.0.0',
    description: 'The most delicious API in town!'
  },
  paths: {
    '/pizzas': {
      get: {
        summary: 'Get all pizzas ๐Ÿ•',
        responses: {
          '200': {
            description: 'List of delicious pizzas',
            content: {
              'application/json': {
                schema: {
                  type: 'array',
                  items: {
                    $ref: '#/components/schemas/Pizza'
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  components: {
    schemas: {
      Pizza: {
        type: 'object',
        properties: {
          id: { type: 'string', example: 'pizza-123' },
          name: { type: 'string', example: 'Margherita Supreme ๐Ÿ…' },
          price: { type: 'number', example: 15.99 },
          toppings: {
            type: 'array',
            items: { type: 'string' },
            example: ['๐Ÿ… Tomato', '๐Ÿง€ Mozzarella', '๐ŸŒฟ Basil']
          }
        },
        required: ['id', 'name', 'price']
      }
    }
  }
};

๐Ÿ’ก Explanation: Notice how we use emojis in our API descriptions to make them more engaging! The $ref creates reusable schemas.

๐ŸŽฏ Express.js with Swagger Integration

Hereโ€™s how to integrate with Express.js:

// ๐Ÿ—๏ธ Express server with Swagger
import express from 'express';
import swaggerJsdoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';

const app = express();

// ๐ŸŽจ Swagger configuration
const swaggerOptions = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'My Awesome API ๐Ÿš€',
      version: '1.0.0',
      description: 'A fantastic TypeScript API!'
    },
    servers: [
      {
        url: 'http://localhost:3000',
        description: 'Development server ๐Ÿ› ๏ธ'
      }
    ]
  },
  apis: ['./src/routes/*.ts']
};

// ๐Ÿ“š Generate documentation
const specs = swaggerJsdoc(swaggerOptions);

// ๐ŸŽฎ Serve Swagger UI
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));

// ๐Ÿ”„ Sample route with JSDoc comments
/**
 * @openapi
 * /users:
 *   get:
 *     summary: Get all users ๐Ÿ‘ฅ
 *     tags: [Users]
 *     responses:
 *       200:
 *         description: List of users
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 $ref: '#/components/schemas/User'
 */
app.get('/users', (req, res) => {
  res.json([
    { id: '1', name: 'Alice ๐Ÿ‘ฉ', email: '[email protected]' },
    { id: '2', name: 'Bob ๐Ÿ‘จ', email: '[email protected]' }
  ]);
});

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce API Documentation

Letโ€™s build comprehensive API documentation for an online store:

// ๐Ÿ›๏ธ E-commerce API types
interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
  inStock: boolean;
  description: string;
  imageUrl: string;
  emoji: string;
}

interface Order {
  id: string;
  userId: string;
  items: OrderItem[];
  total: number;
  status: 'pending' | 'processing' | 'shipped' | 'delivered';
  createdAt: Date;
}

interface OrderItem {
  productId: string;
  quantity: number;
  price: number;
}

// ๐Ÿ“ OpenAPI specification
const ecommerceApiSpec: OpenAPIV3.Document = {
  openapi: '3.0.0',
  info: {
    title: 'ShopMaster API ๐Ÿ›’',
    version: '2.0.0',
    description: 'Your one-stop shop API for all things awesome!',
    contact: {
      name: 'API Support ๐Ÿค',
      email: '[email protected]'
    }
  },
  servers: [
    {
      url: 'https://api.shopmaster.com/v2',
      description: 'Production server ๐Ÿš€'
    },
    {
      url: 'https://staging.shopmaster.com/v2',
      description: 'Staging server ๐Ÿงช'
    }
  ],
  paths: {
    '/products': {
      get: {
        summary: 'Get all products ๐Ÿ›๏ธ',
        description: 'Retrieve a list of all available products',
        tags: ['Products'],
        parameters: [
          {
            name: 'category',
            in: 'query',
            description: 'Filter by category ๐Ÿท๏ธ',
            schema: {
              type: 'string',
              enum: ['electronics', 'clothing', 'books', 'home']
            }
          },
          {
            name: 'inStock',
            in: 'query',
            description: 'Filter by stock availability ๐Ÿ“ฆ',
            schema: {
              type: 'boolean'
            }
          }
        ],
        responses: {
          '200': {
            description: 'Successful response',
            content: {
              'application/json': {
                schema: {
                  type: 'object',
                  properties: {
                    products: {
                      type: 'array',
                      items: { $ref: '#/components/schemas/Product' }
                    },
                    total: { type: 'number' },
                    page: { type: 'number' }
                  }
                }
              }
            }
          },
          '400': {
            description: 'Invalid query parameters',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Error' }
              }
            }
          }
        }
      },
      post: {
        summary: 'Create a new product โž•',
        description: 'Add a new product to the inventory',
        tags: ['Products'],
        security: [{ bearerAuth: [] }],
        requestBody: {
          required: true,
          content: {
            'application/json': {
              schema: { $ref: '#/components/schemas/ProductInput' }
            }
          }
        },
        responses: {
          '201': {
            description: 'Product created successfully ๐ŸŽ‰',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Product' }
              }
            }
          },
          '400': {
            description: 'Invalid product data',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Error' }
              }
            }
          },
          '401': {
            description: 'Unauthorized - Admin access required ๐Ÿ”’'
          }
        }
      }
    },
    '/orders': {
      post: {
        summary: 'Create a new order ๐Ÿ“ฆ',
        description: 'Place a new order with multiple items',
        tags: ['Orders'],
        security: [{ bearerAuth: [] }],
        requestBody: {
          required: true,
          content: {
            'application/json': {
              schema: { $ref: '#/components/schemas/OrderInput' }
            }
          }
        },
        responses: {
          '201': {
            description: 'Order created successfully ๐ŸŽŠ',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Order' }
              }
            }
          }
        }
      }
    }
  },
  components: {
    schemas: {
      Product: {
        type: 'object',
        properties: {
          id: { type: 'string', example: 'prod-123' },
          name: { type: 'string', example: 'Wireless Headphones ๐ŸŽง' },
          price: { type: 'number', format: 'float', example: 99.99 },
          category: { 
            type: 'string', 
            enum: ['electronics', 'clothing', 'books', 'home'],
            example: 'electronics'
          },
          inStock: { type: 'boolean', example: true },
          description: { 
            type: 'string', 
            example: 'Amazing wireless headphones with noise cancellation'
          },
          imageUrl: { 
            type: 'string', 
            format: 'uri',
            example: 'https://example.com/headphones.jpg'
          },
          emoji: { type: 'string', example: '๐ŸŽง' }
        },
        required: ['id', 'name', 'price', 'category', 'inStock']
      },
      ProductInput: {
        type: 'object',
        properties: {
          name: { type: 'string', minLength: 1 },
          price: { type: 'number', minimum: 0 },
          category: { 
            type: 'string', 
            enum: ['electronics', 'clothing', 'books', 'home']
          },
          description: { type: 'string' },
          imageUrl: { type: 'string', format: 'uri' },
          emoji: { type: 'string', maxLength: 2 }
        },
        required: ['name', 'price', 'category']
      },
      Order: {
        type: 'object',
        properties: {
          id: { type: 'string', example: 'order-456' },
          userId: { type: 'string', example: 'user-789' },
          items: {
            type: 'array',
            items: { $ref: '#/components/schemas/OrderItem' }
          },
          total: { type: 'number', format: 'float', example: 199.98 },
          status: {
            type: 'string',
            enum: ['pending', 'processing', 'shipped', 'delivered'],
            example: 'pending'
          },
          createdAt: { 
            type: 'string', 
            format: 'date-time',
            example: '2024-01-15T10:30:00Z'
          }
        }
      },
      OrderItem: {
        type: 'object',
        properties: {
          productId: { type: 'string', example: 'prod-123' },
          quantity: { type: 'integer', minimum: 1, example: 2 },
          price: { type: 'number', format: 'float', example: 99.99 }
        },
        required: ['productId', 'quantity', 'price']
      },
      OrderInput: {
        type: 'object',
        properties: {
          items: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                productId: { type: 'string' },
                quantity: { type: 'integer', minimum: 1 }
              },
              required: ['productId', 'quantity']
            }
          }
        },
        required: ['items']
      },
      Error: {
        type: 'object',
        properties: {
          code: { type: 'string', example: 'INVALID_INPUT' },
          message: { type: 'string', example: 'The provided data is invalid' },
          details: { type: 'array', items: { type: 'string' } }
        }
      }
    },
    securitySchemes: {
      bearerAuth: {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
        description: 'Enter your JWT token ๐Ÿ”‘'
      }
    }
  }
};

๐ŸŽฏ Try it yourself: Add an endpoint for getting order history and include pagination parameters!

๐ŸŽฎ Example 2: Gaming API with Advanced Features

Letโ€™s create documentation for a gaming leaderboard API:

// ๐Ÿ† Gaming API with decorators
import { ApiProperty, ApiResponse, ApiTags } from '@nestjs/swagger';

@ApiTags('๐ŸŽฎ Players')
export class PlayerDto {
  @ApiProperty({ 
    description: 'Unique player identifier',
    example: 'player-007'
  })
  id: string;

  @ApiProperty({ 
    description: 'Player username with emoji',
    example: 'GamerPro ๐ŸŽฏ'
  })
  username: string;

  @ApiProperty({ 
    description: 'Player level',
    minimum: 1,
    maximum: 100,
    example: 42
  })
  level: number;

  @ApiProperty({ 
    description: 'Total experience points',
    example: 12500
  })
  xp: number;

  @ApiProperty({ 
    description: 'Player achievements',
    type: [String],
    example: ['๐Ÿ† First Victory', '๐Ÿ”ฅ Streak Master', '๐Ÿ’Ž Diamond Rank']
  })
  achievements: string[];

  @ApiProperty({ 
    description: 'Online status',
    enum: ['online', 'offline', 'away'],
    example: 'online'
  })
  status: 'online' | 'offline' | 'away';
}

@ApiTags('๐Ÿ† Leaderboard')
export class LeaderboardController {
  @ApiResponse({
    status: 200,
    description: 'Top players retrieved successfully ๐ŸŽŠ',
    type: [PlayerDto]
  })
  @ApiResponse({
    status: 429,
    description: 'Rate limit exceeded โฐ'
  })
  async getTopPlayers(): Promise<PlayerDto[]> {
    return [
      {
        id: 'player-001',
        username: 'ProGamer ๐ŸŽฏ',
        level: 95,
        xp: 89750,
        achievements: ['๐Ÿ† Champion', '๐Ÿ”ฅ Unstoppable', '๐Ÿ’Ž Legend'],
        status: 'online'
      },
      {
        id: 'player-002',
        username: 'SpeedRunner โšก',
        level: 87,
        xp: 76230,
        achievements: ['๐Ÿƒ Speed Demon', 'โฑ๏ธ Time Master'],
        status: 'away'
      }
    ];
  }
}

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Advanced Topic 1: Custom Validation and Schemas

When youโ€™re ready to level up, try advanced schema validation:

// ๐ŸŽฏ Advanced schema with custom validation
const advancedSchema: OpenAPIV3.SchemaObject = {
  type: 'object',
  properties: {
    email: {
      type: 'string',
      format: 'email',
      pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
      example: '[email protected]'
    },
    age: {
      type: 'integer',
      minimum: 13,
      maximum: 120,
      example: 25
    },
    preferences: {
      type: 'object',
      properties: {
        theme: {
          type: 'string',
          enum: ['light', 'dark', 'auto'],
          default: 'auto'
        },
        notifications: {
          type: 'object',
          properties: {
            email: { type: 'boolean', default: true },
            push: { type: 'boolean', default: false },
            sms: { type: 'boolean', default: false }
          },
          additionalProperties: false
        }
      }
    }
  },
  required: ['email', 'age'],
  additionalProperties: false
};

// ๐Ÿช„ Conditional schemas based on discriminator
const paymentMethodSchema: OpenAPIV3.SchemaObject = {
  oneOf: [
    {
      type: 'object',
      properties: {
        type: { type: 'string', enum: ['credit_card'] },
        cardNumber: { type: 'string', pattern: '^[0-9]{16}$' },
        expiryDate: { type: 'string', pattern: '^(0[1-9]|1[0-2])\/[0-9]{2}$' },
        cvv: { type: 'string', pattern: '^[0-9]{3,4}$' }
      },
      required: ['type', 'cardNumber', 'expiryDate', 'cvv']
    },
    {
      type: 'object',
      properties: {
        type: { type: 'string', enum: ['paypal'] },
        email: { type: 'string', format: 'email' }
      },
      required: ['type', 'email']
    }
  ],
  discriminator: {
    propertyName: 'type'
  }
};

๐Ÿ—๏ธ Advanced Topic 2: Code Generation and Automation

For the brave developers who want to automate everything:

// ๐Ÿš€ Automated client generation
import { generateApi } from 'swagger-typescript-api';

const generateClient = async () => {
  const { files } = await generateApi({
    name: 'MyApi.ts',
    url: 'http://localhost:3000/api-docs-json',
    generateClient: true,
    generateRouteTypes: true,
    generateResponses: true,
    toJS: false,
    extractRequestParams: true,
    extractRequestBody: true,
    extractEnums: true,
    prettier: {
      printWidth: 120,
      tabWidth: 2,
      trailingComma: 'all',
      singleQuote: true
    }
  });

  console.log('๐ŸŽ‰ Generated client files:', files);
};

// ๐Ÿ”ง Custom code generation with templates
const customTemplate = `
// ๐ŸŽจ Auto-generated API client
export class {{className}}Api {
  constructor(private baseUrl: string) {}

  {{#operations}}
  async {{operationId}}({{#parameters}}{{paramName}}: {{dataType}}{{#hasMore}}, {{/hasMore}}{{/parameters}}): Promise<{{returnType}}> {
    const response = await fetch(\`\${this.baseUrl}{{path}}\`, {
      method: '{{httpMethod}}',
      headers: {
        'Content-Type': 'application/json',
        {{#hasAuth}}'Authorization': \`Bearer \${this.token}\`{{/hasAuth}}
      },
      {{#hasBody}}body: JSON.stringify({{bodyParam}}){{/hasBody}}
    });
    
    if (!response.ok) {
      throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
    }
    
    return response.json();
  }
  {{/operations}}
}
`;

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Inconsistent Documentation

// โŒ Wrong way - outdated documentation!
/**
 * @openapi
 * /users:
 *   get:
 *     summary: Get all users
 *     responses:
 *       200:
 *         description: List of users
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 type: object
 *                 properties:
 *                   id: { type: string }
 *                   name: { type: string }
 */
app.get('/users', (req, res) => {
  // ๐Ÿ’ฅ Actual response has different structure!
  res.json([
    { 
      userId: '1', 
      fullName: 'Alice', 
      email: '[email protected]',
      createdAt: new Date()
    }
  ]);
});

// โœ… Correct way - keep docs in sync!
/**
 * @openapi
 * /users:
 *   get:
 *     summary: Get all users ๐Ÿ‘ฅ
 *     responses:
 *       200:
 *         description: List of users
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 $ref: '#/components/schemas/User'
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       properties:
 *         userId: { type: string, example: "user-123" }
 *         fullName: { type: string, example: "Alice Wonder" }
 *         email: { type: string, format: email }
 *         createdAt: { type: string, format: date-time }
 */
app.get('/users', (req, res) => {
  res.json([
    {
      userId: 'user-123',
      fullName: 'Alice Wonder',
      email: '[email protected]',
      createdAt: new Date().toISOString()
    }
  ]);
});

๐Ÿคฏ Pitfall 2: Missing Error Responses

// โŒ Dangerous - no error documentation!
/**
 * @openapi
 * /users/{id}:
 *   get:
 *     summary: Get user by ID
 *     responses:
 *       200:
 *         description: User found
 */
app.get('/users/:id', (req, res) => {
  // ๐Ÿ’ฅ What if user not found? No documentation!
  const user = findUserById(req.params.id);
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }
  res.json(user);
});

// โœ… Safe - document all possible responses!
/**
 * @openapi
 * /users/{id}:
 *   get:
 *     summary: Get user by ID ๐Ÿ”
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: User identifier
 *     responses:
 *       200:
 *         description: User found successfully โœ…
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/User'
 *       404:
 *         description: User not found โŒ
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Error'
 *       400:
 *         description: Invalid user ID format โš ๏ธ
 */
app.get('/users/:id', (req, res) => {
  const { id } = req.params;
  
  if (!isValidId(id)) {
    return res.status(400).json({
      error: 'INVALID_ID',
      message: 'User ID must be a valid format'
    });
  }
  
  const user = findUserById(id);
  if (!user) {
    return res.status(404).json({
      error: 'USER_NOT_FOUND',
      message: 'No user found with the specified ID'
    });
  }
  
  res.json(user);
});

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Keep Documentation Updated: Use automation to sync docs with code
  2. ๐Ÿ“ Write Descriptive Summaries: Make each endpointโ€™s purpose clear
  3. ๐Ÿ›ก๏ธ Document Error Cases: Include all possible error responses
  4. ๐ŸŽจ Use Examples Liberally: Show real data in your examples
  5. โœจ Version Your API: Use semantic versioning for API changes
  6. ๐Ÿ”ง Validate Your Spec: Use OpenAPI validators
  7. ๐Ÿš€ Generate Clients: Automate client library generation
  8. ๐Ÿ“Š Monitor Usage: Track which endpoints are actually used

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Complete Movie Database API Documentation

Create comprehensive API documentation for a movie database:

๐Ÿ“‹ Requirements:

  • โœ… Movie CRUD operations (Create, Read, Update, Delete)
  • ๐ŸŽฌ Movie search with filters (genre, year, rating)
  • ๐Ÿ‘ค User authentication and authorization
  • โญ Movie rating and review system
  • ๐Ÿ“Š Statistics endpoint for admin users
  • ๐ŸŽจ Use fun movie-themed examples with emojis!

๐Ÿš€ Bonus Points:

  • Add pagination for large result sets
  • Include file upload for movie posters
  • Create webhook endpoints for real-time updates
  • Add rate limiting documentation

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐ŸŽฏ Complete Movie Database API Documentation!
const movieApiSpec: OpenAPIV3.Document = {
  openapi: '3.0.0',
  info: {
    title: 'CinemaHub API ๐ŸŽฌ',
    version: '1.0.0',
    description: 'The ultimate movie database API for film enthusiasts!',
    contact: {
      name: 'CinemaHub Support ๐ŸŽญ',
      email: '[email protected]'
    },
    license: {
      name: 'MIT',
      url: 'https://opensource.org/licenses/MIT'
    }
  },
  servers: [
    {
      url: 'https://api.cinemahub.com/v1',
      description: 'Production server ๐Ÿš€'
    }
  ],
  paths: {
    '/movies': {
      get: {
        summary: 'Search movies ๐Ÿ”',
        description: 'Search and filter movies in the database',
        tags: ['Movies'],
        parameters: [
          {
            name: 'query',
            in: 'query',
            description: 'Search query for movie titles',
            schema: { type: 'string' },
            example: 'Avengers'
          },
          {
            name: 'genre',
            in: 'query',
            description: 'Filter by genre ๐ŸŽญ',
            schema: {
              type: 'string',
              enum: ['action', 'comedy', 'drama', 'horror', 'sci-fi', 'romance']
            }
          },
          {
            name: 'year',
            in: 'query',
            description: 'Filter by release year ๐Ÿ“…',
            schema: { type: 'integer', minimum: 1900, maximum: 2024 }
          },
          {
            name: 'rating',
            in: 'query',
            description: 'Minimum rating (1-10) โญ',
            schema: { type: 'number', minimum: 1, maximum: 10 }
          },
          {
            name: 'page',
            in: 'query',
            description: 'Page number for pagination ๐Ÿ“„',
            schema: { type: 'integer', minimum: 1, default: 1 }
          },
          {
            name: 'limit',
            in: 'query',
            description: 'Number of items per page',
            schema: { type: 'integer', minimum: 1, maximum: 100, default: 20 }
          }
        ],
        responses: {
          '200': {
            description: 'Movies retrieved successfully ๐ŸŽ‰',
            content: {
              'application/json': {
                schema: {
                  type: 'object',
                  properties: {
                    movies: {
                      type: 'array',
                      items: { $ref: '#/components/schemas/Movie' }
                    },
                    pagination: { $ref: '#/components/schemas/Pagination' }
                  }
                }
              }
            }
          },
          '400': {
            description: 'Invalid query parameters',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Error' }
              }
            }
          }
        }
      },
      post: {
        summary: 'Add a new movie ๐ŸŽฌ',
        description: 'Create a new movie entry (Admin only)',
        tags: ['Movies'],
        security: [{ bearerAuth: [] }],
        requestBody: {
          required: true,
          content: {
            'application/json': {
              schema: { $ref: '#/components/schemas/MovieInput' }
            }
          }
        },
        responses: {
          '201': {
            description: 'Movie created successfully ๐ŸŽŠ',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Movie' }
              }
            }
          },
          '400': {
            description: 'Invalid movie data',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Error' }
              }
            }
          },
          '401': {
            description: 'Unauthorized - Admin access required ๐Ÿ”’'
          }
        }
      }
    },
    '/movies/{id}': {
      get: {
        summary: 'Get movie details ๐ŸŽฌ',
        description: 'Retrieve detailed information about a specific movie',
        tags: ['Movies'],
        parameters: [
          {
            name: 'id',
            in: 'path',
            required: true,
            description: 'Movie ID',
            schema: { type: 'string' },
            example: 'movie-123'
          }
        ],
        responses: {
          '200': {
            description: 'Movie details retrieved successfully',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/MovieDetailed' }
              }
            }
          },
          '404': {
            description: 'Movie not found ๐Ÿ˜ž'
          }
        }
      }
    },
    '/movies/{id}/reviews': {
      get: {
        summary: 'Get movie reviews ๐Ÿ“',
        description: 'Retrieve reviews for a specific movie',
        tags: ['Reviews'],
        parameters: [
          {
            name: 'id',
            in: 'path',
            required: true,
            description: 'Movie ID',
            schema: { type: 'string' }
          }
        ],
        responses: {
          '200': {
            description: 'Reviews retrieved successfully',
            content: {
              'application/json': {
                schema: {
                  type: 'array',
                  items: { $ref: '#/components/schemas/Review' }
                }
              }
            }
          }
        }
      },
      post: {
        summary: 'Add a movie review โญ',
        description: 'Submit a review for a movie',
        tags: ['Reviews'],
        security: [{ bearerAuth: [] }],
        parameters: [
          {
            name: 'id',
            in: 'path',
            required: true,
            description: 'Movie ID',
            schema: { type: 'string' }
          }
        ],
        requestBody: {
          required: true,
          content: {
            'application/json': {
              schema: { $ref: '#/components/schemas/ReviewInput' }
            }
          }
        },
        responses: {
          '201': {
            description: 'Review added successfully ๐ŸŽ‰',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/Review' }
              }
            }
          }
        }
      }
    }
  },
  components: {
    schemas: {
      Movie: {
        type: 'object',
        properties: {
          id: { type: 'string', example: 'movie-123' },
          title: { type: 'string', example: 'The Awesome Movie ๐ŸŽฌ' },
          description: { 
            type: 'string',
            example: 'An incredible adventure that will blow your mind!'
          },
          genre: { 
            type: 'array',
            items: { type: 'string' },
            example: ['action', 'adventure', 'sci-fi']
          },
          releaseYear: { type: 'integer', example: 2024 },
          rating: { type: 'number', format: 'float', example: 8.5 },
          duration: { type: 'integer', example: 142 },
          director: { type: 'string', example: 'Steven Spielberg ๐ŸŽญ' },
          cast: {
            type: 'array',
            items: { type: 'string' },
            example: ['Actor One ๐ŸŽญ', 'Actor Two ๐ŸŒŸ', 'Actor Three โญ']
          },
          posterUrl: { 
            type: 'string',
            format: 'uri',
            example: 'https://example.com/poster.jpg'
          },
          createdAt: { type: 'string', format: 'date-time' },
          updatedAt: { type: 'string', format: 'date-time' }
        },
        required: ['id', 'title', 'genre', 'releaseYear', 'rating']
      },
      MovieDetailed: {
        allOf: [
          { $ref: '#/components/schemas/Movie' },
          {
            type: 'object',
            properties: {
              reviews: {
                type: 'array',
                items: { $ref: '#/components/schemas/Review' }
              },
              averageRating: { type: 'number', format: 'float' },
              reviewCount: { type: 'integer' }
            }
          }
        ]
      },
      MovieInput: {
        type: 'object',
        properties: {
          title: { type: 'string', minLength: 1 },
          description: { type: 'string' },
          genre: {
            type: 'array',
            items: {
              type: 'string',
              enum: ['action', 'comedy', 'drama', 'horror', 'sci-fi', 'romance']
            }
          },
          releaseYear: { type: 'integer', minimum: 1900, maximum: 2024 },
          duration: { type: 'integer', minimum: 1 },
          director: { type: 'string' },
          cast: {
            type: 'array',
            items: { type: 'string' }
          },
          posterUrl: { type: 'string', format: 'uri' }
        },
        required: ['title', 'genre', 'releaseYear']
      },
      Review: {
        type: 'object',
        properties: {
          id: { type: 'string', example: 'review-456' },
          movieId: { type: 'string', example: 'movie-123' },
          userId: { type: 'string', example: 'user-789' },
          username: { type: 'string', example: 'MovieLover123 ๐Ÿฟ' },
          rating: { type: 'integer', minimum: 1, maximum: 10, example: 9 },
          comment: { 
            type: 'string',
            example: 'Absolutely amazing! A masterpiece of cinema ๐ŸŽฌโœจ'
          },
          createdAt: { type: 'string', format: 'date-time' }
        },
        required: ['id', 'movieId', 'userId', 'rating']
      },
      ReviewInput: {
        type: 'object',
        properties: {
          rating: { type: 'integer', minimum: 1, maximum: 10 },
          comment: { type: 'string', maxLength: 1000 }
        },
        required: ['rating']
      },
      Pagination: {
        type: 'object',
        properties: {
          page: { type: 'integer', example: 1 },
          limit: { type: 'integer', example: 20 },
          total: { type: 'integer', example: 150 },
          pages: { type: 'integer', example: 8 }
        }
      },
      Error: {
        type: 'object',
        properties: {
          code: { type: 'string', example: 'VALIDATION_ERROR' },
          message: { type: 'string', example: 'The provided data is invalid' },
          details: {
            type: 'array',
            items: { type: 'string' },
            example: ['Title is required', 'Genre must be valid']
          }
        }
      }
    },
    securitySchemes: {
      bearerAuth: {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
        description: 'Enter your JWT token ๐Ÿ”‘'
      }
    }
  }
};

// ๐ŸŽฎ Express.js implementation
app.get('/movies', async (req, res) => {
  const { query, genre, year, rating, page = 1, limit = 20 } = req.query;
  
  try {
    const movies = await movieService.searchMovies({
      query: query as string,
      genre: genre as string,
      year: year ? parseInt(year as string) : undefined,
      rating: rating ? parseFloat(rating as string) : undefined,
      page: parseInt(page as string),
      limit: parseInt(limit as string)
    });
    
    res.json({
      movies: movies.data,
      pagination: {
        page: movies.page,
        limit: movies.limit,
        total: movies.total,
        pages: Math.ceil(movies.total / movies.limit)
      }
    });
  } catch (error) {
    res.status(400).json({
      code: 'SEARCH_ERROR',
      message: 'Failed to search movies',
      details: [error.message]
    });
  }
});

๐ŸŽ“ Key Takeaways

Youโ€™ve learned so much! Hereโ€™s what you can now do:

  • โœ… Create comprehensive API documentation with confidence ๐Ÿ’ช
  • โœ… Avoid common mistakes that trip up API developers ๐Ÿ›ก๏ธ
  • โœ… Apply best practices in real projects ๐ŸŽฏ
  • โœ… Debug documentation issues like a pro ๐Ÿ›
  • โœ… Build awesome APIs with TypeScript! ๐Ÿš€

Remember: Good API documentation is like a friendly guide that helps developers succeed with your API. Itโ€™s not just about the code - itโ€™s about creating a great developer experience! ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered API Documentation with Swagger/OpenAPI!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice documenting your own APIs
  2. ๐Ÿ—๏ธ Set up automated documentation generation
  3. ๐Ÿ“š Move on to our next tutorial: Authentication & JWT
  4. ๐ŸŒŸ Share your beautiful API docs with the community!

Remember: Every great API starts with great documentation. Keep building, keep documenting, and most importantly, have fun! ๐Ÿš€


Happy coding! ๐ŸŽ‰๐Ÿš€โœจ