+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 122 of 355

🔧 REST API Types: Generating from OpenAPI

Master automated TypeScript type generation from OpenAPI specifications for type-safe REST API development and seamless frontend-backend integration 🚀

💎Advanced
25 min read

Prerequisites

  • Strong understanding of REST APIs and HTTP methods 📝
  • Experience with TypeScript interfaces and generics ⚡
  • Basic knowledge of OpenAPI/Swagger specifications 💻

What you'll learn

  • Master OpenAPI specification for comprehensive API documentation 🎯
  • Generate type-safe TypeScript clients from OpenAPI schemas 🏗️
  • Build automated workflows for API type synchronization 🐛
  • Create enterprise-ready API development patterns with tooling ✨

🎯 Introduction

Welcome to the future of API development! 🔧 If manual type definition is like hand-crafting each screw for a machine, then OpenAPI type generation is like having an automated factory that produces perfectly fitting TypeScript types from your API specifications!

OpenAPI (formerly Swagger) specifications serve as the single source of truth for your REST APIs, and with modern tooling, you can automatically generate TypeScript types, client libraries, and even complete SDK packages. This approach eliminates type mismatches between frontend and backend, reduces maintenance overhead, and ensures your API contracts are always up-to-date.

By the end of this tutorial, you’ll be a master of API type generation, capable of building completely automated workflows that keep your TypeScript applications perfectly synchronized with your REST API specifications. Let’s generate some type safety! ⚡

📚 Understanding OpenAPI and Type Generation

🤔 What Is OpenAPI?

OpenAPI Specification (OAS) is a standard for describing REST APIs. It defines endpoints, request/response schemas, authentication methods, and more in a machine-readable format that can be used to generate documentation, client SDKs, and TypeScript types.

# 🌟 Basic OpenAPI specification
openapi: 3.0.3
info:
  title: User Management API
  description: A comprehensive API for managing users and their data
  version: 1.0.0
  contact:
    name: API Support
    email: [email protected]
servers:
  - url: https://api.example.com/v1
    description: Production server
  - url: https://staging-api.example.com/v1
    description: Staging server

# 📦 Reusable components
components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - name
      properties:
        id:
          type: integer
          format: int64
          description: Unique identifier for the user
          example: 12345
        email:
          type: string
          format: email
          description: User's email address
          example: [email protected]
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: User's full name
          example: John Doe
        age:
          type: integer
          minimum: 0
          maximum: 150
          description: User's age in years
          example: 30
        role:
          type: string
          enum: [admin, user, moderator]
          description: User's role in the system
          example: user
        isActive:
          type: boolean
          description: Whether the user account is active
          example: true
        metadata:
          type: object
          additionalProperties: true
          description: Additional user metadata
        createdAt:
          type: string
          format: date-time
          description: When the user was created
          example: "2023-01-01T12:00:00Z"
        updatedAt:
          type: string
          format: date-time
          description: When the user was last updated
          example: "2023-01-02T12:00:00Z"

    CreateUserRequest:
      type: object
      required:
        - email
        - name
      properties:
        email:
          type: string
          format: email
          description: User's email address
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: User's full name
        age:
          type: integer
          minimum: 0
          maximum: 150
          description: User's age in years
        role:
          type: string
          enum: [admin, user, moderator]
          default: user
          description: User's role in the system
        metadata:
          type: object
          additionalProperties: true
          description: Additional user metadata

    UpdateUserRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: User's full name
        age:
          type: integer
          minimum: 0
          maximum: 150
          description: User's age in years
        role:
          type: string
          enum: [admin, user, moderator]
          description: User's role in the system
        isActive:
          type: boolean
          description: Whether the user account is active
        metadata:
          type: object
          additionalProperties: true
          description: Additional user metadata

    ApiResponse:
      type: object
      required:
        - success
        - message
      properties:
        success:
          type: boolean
          description: Whether the operation was successful
        message:
          type: string
          description: Human-readable message about the operation
        data:
          description: The response data (varies by endpoint)
        errors:
          type: array
          items:
            type: string
          description: List of error messages if operation failed

    PaginatedResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          description: Array of items for the current page
        pagination:
          type: object
          required:
            - page
            - limit
            - total
            - pages
          properties:
            page:
              type: integer
              minimum: 1
              description: Current page number
            limit:
              type: integer
              minimum: 1
              maximum: 100
              description: Number of items per page
            total:
              type: integer
              minimum: 0
              description: Total number of items
            pages:
              type: integer
              minimum: 0
              description: Total number of pages

  parameters:
    PageParam:
      name: page
      in: query
      description: Page number for pagination
      required: false
      schema:
        type: integer
        minimum: 1
        default: 1

    LimitParam:
      name: limit
      in: query
      description: Number of items per page
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20

    UserIdParam:
      name: userId
      in: path
      description: User ID
      required: true
      schema:
        type: integer
        format: int64

# 🎯 API endpoints
paths:
  /users:
    get:
      summary: List users
      description: Retrieve a paginated list of users
      operationId: listUsers
      tags:
        - Users
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/LimitParam'
        - name: role
          in: query
          description: Filter users by role
          required: false
          schema:
            type: string
            enum: [admin, user, moderator]
        - name: isActive
          in: query
          description: Filter users by active status
          required: false
          schema:
            type: boolean
        - name: search
          in: query
          description: Search users by name or email
          required: false
          schema:
            type: string
            minLength: 1
      responses:
        '200':
          description: List of users retrieved successfully
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiResponse'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - $ref: '#/components/schemas/PaginatedResponse'
                          - type: object
                            properties:
                              data:
                                type: array
                                items:
                                  $ref: '#/components/schemas/User'
        '400':
          description: Bad request - invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'

    post:
      summary: Create user
      description: Create a new user account
      operationId: createUser
      tags:
        - Users
      requestBody:
        description: User data for creation
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: User created successfully
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/User'
        '400':
          description: Bad request - validation errors
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '409':
          description: Conflict - user already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'

  /users/{userId}:
    get:
      summary: Get user by ID
      description: Retrieve a specific user by their ID
      operationId: getUserById
      tags:
        - Users
      parameters:
        - $ref: '#/components/parameters/UserIdParam'
      responses:
        '200':
          description: User retrieved successfully
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/User'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'

    put:
      summary: Update user
      description: Update an existing user's information
      operationId: updateUser
      tags:
        - Users
      parameters:
        - $ref: '#/components/parameters/UserIdParam'
      requestBody:
        description: Updated user data
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUserRequest'
      responses:
        '200':
          description: User updated successfully
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/User'
        '400':
          description: Bad request - validation errors
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'

    delete:
      summary: Delete user
      description: Delete a user account
      operationId: deleteUser
      tags:
        - Users
      parameters:
        - $ref: '#/components/parameters/UserIdParam'
      responses:
        '200':
          description: User deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'

🛠️ Setting Up Type Generation Tools

📦 Installing OpenAPI Generators

There are several excellent tools for generating TypeScript types from OpenAPI specifications. Let’s explore the most powerful ones:

// 🌟 Package.json setup for type generation
{
  "name": "api-client-example",
  "version": "1.0.0",
  "scripts": {
    "generate:types": "openapi-typescript openapi.yaml --output ./src/types/api.ts",
    "generate:client": "openapi-codegen",
    "generate:all": "npm run generate:types && npm run generate:client",
    "build": "npm run generate:all && tsc",
    "dev": "npm run generate:all && tsc --watch"
  },
  "devDependencies": {
    "@openapitools/openapi-generator-cli": "^2.7.0",
    "openapi-typescript": "^6.7.1",
    "openapi-typescript-codegen": "^0.25.0",
    "swagger-typescript-api": "^13.0.3",
    "orval": "^6.20.0",
    "typescript": "^5.3.0",
    "axios": "^1.6.0"
  },
  "dependencies": {
    "ky": "^1.0.0"
  }
}

🎯 OpenAPI TypeScript Generator

The most straightforward approach uses openapi-typescript to generate type definitions:

// 🚀 Generated types from OpenAPI spec
// src/types/api.ts (generated automatically)

export interface paths {
  "/users": {
    get: operations["listUsers"];
    post: operations["createUser"];
  };
  "/users/{userId}": {
    get: operations["getUserById"];
    put: operations["updateUser"];
    delete: operations["deleteUser"];
  };
}

export interface components {
  schemas: {
    User: {
      id: number;
      email: string;
      name: string;
      age?: number;
      role?: "admin" | "user" | "moderator";
      isActive?: boolean;
      metadata?: Record<string, any>;
      createdAt?: string;
      updatedAt?: string;
    };
    CreateUserRequest: {
      email: string;
      name: string;
      age?: number;
      role?: "admin" | "user" | "moderator";
      metadata?: Record<string, any>;
    };
    UpdateUserRequest: {
      name?: string;
      age?: number;
      role?: "admin" | "user" | "moderator";
      isActive?: boolean;
      metadata?: Record<string, any>;
    };
    ApiResponse: {
      success: boolean;
      message: string;
      data?: any;
      errors?: string[];
    };
    PaginatedResponse: {
      data: any[];
      pagination: {
        page: number;
        limit: number;
        total: number;
        pages: number;
      };
    };
  };
  parameters: {
    PageParam: number;
    LimitParam: number;
    UserIdParam: number;
  };
}

export interface operations {
  listUsers: {
    parameters: {
      query?: {
        page?: number;
        limit?: number;
        role?: "admin" | "user" | "moderator";
        isActive?: boolean;
        search?: string;
      };
    };
    responses: {
      200: {
        content: {
          "application/json": components["schemas"]["ApiResponse"] & {
            data: components["schemas"]["PaginatedResponse"] & {
              data: components["schemas"]["User"][];
            };
          };
        };
      };
      400: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      500: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
    };
  };
  
  createUser: {
    requestBody: {
      content: {
        "application/json": components["schemas"]["CreateUserRequest"];
      };
    };
    responses: {
      201: {
        content: {
          "application/json": components["schemas"]["ApiResponse"] & {
            data: components["schemas"]["User"];
          };
        };
      };
      400: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      409: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      500: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
    };
  };

  getUserById: {
    parameters: {
      path: {
        userId: number;
      };
    };
    responses: {
      200: {
        content: {
          "application/json": components["schemas"]["ApiResponse"] & {
            data: components["schemas"]["User"];
          };
        };
      };
      404: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      500: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
    };
  };

  updateUser: {
    parameters: {
      path: {
        userId: number;
      };
    };
    requestBody: {
      content: {
        "application/json": components["schemas"]["UpdateUserRequest"];
      };
    };
    responses: {
      200: {
        content: {
          "application/json": components["schemas"]["ApiResponse"] & {
            data: components["schemas"]["User"];
          };
        };
      };
      400: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      404: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      500: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
    };
  };

  deleteUser: {
    parameters: {
      path: {
        userId: number;
      };
    };
    responses: {
      200: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      404: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
      500: {
        content: {
          "application/json": components["schemas"]["ApiResponse"];
        };
      };
    };
  };
}

// 🎯 Utility types for easier usage
export type User = components["schemas"]["User"];
export type CreateUserRequest = components["schemas"]["CreateUserRequest"];
export type UpdateUserRequest = components["schemas"]["UpdateUserRequest"];
export type ApiResponse<T = any> = components["schemas"]["ApiResponse"] & { data?: T };
export type PaginatedResponse<T = any> = components["schemas"]["PaginatedResponse"] & { data: T[] };

// 📤 Request/Response types for each operation
export type ListUsersParams = operations["listUsers"]["parameters"]["query"];
export type ListUsersResponse = operations["listUsers"]["responses"]["200"]["content"]["application/json"];
export type CreateUserParams = operations["createUser"]["requestBody"]["content"]["application/json"];
export type CreateUserResponse = operations["createUser"]["responses"]["201"]["content"]["application/json"];
export type GetUserParams = operations["getUserById"]["parameters"]["path"];
export type GetUserResponse = operations["getUserById"]["responses"]["200"]["content"]["application/json"];
export type UpdateUserParams = operations["updateUser"]["parameters"]["path"] & {
  body: operations["updateUser"]["requestBody"]["content"]["application/json"];
};
export type UpdateUserResponse = operations["updateUser"]["responses"]["200"]["content"]["application/json"];
export type DeleteUserParams = operations["deleteUser"]["parameters"]["path"];
export type DeleteUserResponse = operations["deleteUser"]["responses"]["200"]["content"]["application/json"];

🏗️ Building Type-Safe API Clients

🎯 Basic API Client Implementation

Now let’s create a type-safe API client using our generated types:

// 🌟 Type-safe API client implementation
// src/api/client.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import type {
  User,
  CreateUserRequest,
  UpdateUserRequest,
  ListUsersParams,
  ListUsersResponse,
  CreateUserResponse,
  GetUserResponse,
  UpdateUserResponse,
  DeleteUserResponse,
  ApiResponse,
} from '../types/api';

// 📦 Configuration interface
interface ApiClientConfig {
  baseURL: string;
  timeout?: number;
  apiKey?: string;
  retries?: number;
}

// 🚀 Type-safe API client class
export class UserApiClient {
  private client: AxiosInstance;
  private retries: number;

  constructor(config: ApiClientConfig) {
    this.retries = config.retries ?? 3;
    
    this.client = axios.create({
      baseURL: config.baseURL,
      timeout: config.timeout ?? 10000,
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        ...(config.apiKey && { 'Authorization': `Bearer ${config.apiKey}` }),
      },
    });

    this.setupInterceptors();
  }

  // 🔧 Setup request/response interceptors
  private setupInterceptors(): void {
    // Request interceptor
    this.client.interceptors.request.use(
      (config) => {
        console.log(`📡 Making ${config.method?.toUpperCase()} request to ${config.url}`);
        return config;
      },
      (error) => {
        console.error('❌ Request error:', error);
        return Promise.reject(error);
      }
    );

    // Response interceptor
    this.client.interceptors.response.use(
      (response) => {
        console.log(`✅ Response received: ${response.status} ${response.statusText}`);
        return response;
      },
      async (error) => {
        const originalRequest = error.config;
        
        // Auto-retry logic
        if (originalRequest && !originalRequest._retry && originalRequest._retryCount < this.retries) {
          originalRequest._retry = true;
          originalRequest._retryCount = (originalRequest._retryCount || 0) + 1;
          
          console.log(`🔄 Retrying request (attempt ${originalRequest._retryCount}/${this.retries})`);
          
          // Exponential backoff
          const delay = Math.pow(2, originalRequest._retryCount) * 1000;
          await new Promise(resolve => setTimeout(resolve, delay));
          
          return this.client(originalRequest);
        }
        
        console.error('❌ Response error:', error);
        return Promise.reject(error);
      }
    );
  }

  // 🎯 Generic request method with full type safety
  private async request<T>(
    method: 'GET' | 'POST' | 'PUT' | 'DELETE',
    endpoint: string,
    options: AxiosRequestConfig = {}
  ): Promise<T> {
    try {
      const response: AxiosResponse<T> = await this.client.request({
        method,
        url: endpoint,
        ...options,
      });
      
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        const errorMessage = error.response?.data?.message || error.message;
        throw new Error(`API Error: ${errorMessage}`);
      }
      throw error;
    }
  }

  // 📋 List users with type-safe parameters
  async listUsers(params?: ListUsersParams): Promise<ListUsersResponse> {
    console.log('👥 Fetching user list...');
    
    const response = await this.request<ListUsersResponse>('GET', '/users', {
      params,
    });
    
    console.log(`✅ Retrieved ${response.data.data.length} users`);
    return response;
  }

  // 👤 Get user by ID
  async getUserById(userId: number): Promise<GetUserResponse> {
    console.log(`🔍 Fetching user ${userId}...`);
    
    const response = await this.request<GetUserResponse>('GET', `/users/${userId}`);
    
    console.log(`✅ Retrieved user: ${response.data.email}`);
    return response;
  }

  // ➕ Create new user
  async createUser(userData: CreateUserRequest): Promise<CreateUserResponse> {
    console.log(`👤 Creating user: ${userData.email}...`);
    
    const response = await this.request<CreateUserResponse>('POST', '/users', {
      data: userData,
    });
    
    console.log(`✅ Created user with ID: ${response.data.id}`);
    return response;
  }

  // ✏️ Update existing user
  async updateUser(userId: number, userData: UpdateUserRequest): Promise<UpdateUserResponse> {
    console.log(`📝 Updating user ${userId}...`);
    
    const response = await this.request<UpdateUserResponse>('PUT', `/users/${userId}`, {
      data: userData,
    });
    
    console.log(`✅ Updated user: ${response.data.email}`);
    return response;
  }

  // 🗑️ Delete user
  async deleteUser(userId: number): Promise<DeleteUserResponse> {
    console.log(`🗑️ Deleting user ${userId}...`);
    
    const response = await this.request<DeleteUserResponse>('DELETE', `/users/${userId}`);
    
    console.log('✅ User deleted successfully');
    return response;
  }

  // 🔍 Search users with advanced filtering
  async searchUsers(searchTerm: string, filters?: Partial<ListUsersParams>): Promise<User[]> {
    console.log(`🔍 Searching users with term: "${searchTerm}"...`);
    
    const response = await this.listUsers({
      search: searchTerm,
      ...filters,
    });
    
    console.log(`✅ Found ${response.data.data.length} matching users`);
    return response.data.data;
  }

  // 📊 Get users with pagination support
  async getUsersPaginated(page: number = 1, limit: number = 20): Promise<{
    users: User[];
    pagination: ListUsersResponse['data']['pagination'];
  }> {
    console.log(`📄 Fetching page ${page} with ${limit} users...`);
    
    const response = await this.listUsers({ page, limit });
    
    return {
      users: response.data.data,
      pagination: response.data.pagination,
    };
  }
}

// 🎮 Usage examples
const exampleUsage = async (): Promise<void> => {
  // 🏗️ Initialize the API client
  const apiClient = new UserApiClient({
    baseURL: 'https://api.example.com/v1',
    apiKey: 'your-api-key-here',
    timeout: 15000,
    retries: 3,
  });

  try {
    // 📋 List all users
    const userList = await apiClient.listUsers();
    console.log('👥 All users:', userList.data.data);

    // 🔍 Get specific user
    const user = await apiClient.getUserById(123);
    console.log('👤 User details:', user.data);

    // ➕ Create new user
    const newUser = await apiClient.createUser({
      email: '[email protected]',
      name: 'Jane Doe',
      age: 28,
      role: 'user',
      metadata: {
        department: 'Engineering',
        location: 'San Francisco',
      },
    });
    console.log('✅ Created user:', newUser.data);

    // ✏️ Update user
    const updatedUser = await apiClient.updateUser(newUser.data.id, {
      name: 'Jane Smith',
      role: 'moderator',
      isActive: true,
    });
    console.log('📝 Updated user:', updatedUser.data);

    // 🔍 Search users
    const searchResults = await apiClient.searchUsers('Jane', {
      role: 'moderator',
      isActive: true,
    });
    console.log('🔍 Search results:', searchResults);

    // 📄 Paginated results
    const { users, pagination } = await apiClient.getUsersPaginated(1, 10);
    console.log('📄 Page 1 users:', users);
    console.log('📊 Pagination info:', pagination);

    // 🗑️ Delete user
    await apiClient.deleteUser(newUser.data.id);
    console.log('🗑️ User deleted successfully');

  } catch (error) {
    console.error('💥 API Error:', error);
  }
};

🎨 Advanced Client Features

Let’s enhance our API client with advanced features like caching, request deduplication, and real-time updates:

// 🚀 Advanced API client with caching and real-time features
// src/api/advanced-client.ts
import { EventEmitter } from 'events';
import type { User, CreateUserRequest, UpdateUserRequest, ListUsersParams } from '../types/api';

// 📦 Cache interface
interface CacheEntry<T> {
  data: T;
  timestamp: number;
  ttl: number;
}

// 🎯 Advanced API client with caching
export class AdvancedUserApiClient extends EventEmitter {
  private cache = new Map<string, CacheEntry<any>>();
  private pendingRequests = new Map<string, Promise<any>>();
  private defaultTTL = 5 * 60 * 1000; // 5 minutes

  constructor(private baseClient: UserApiClient) {
    super();
    this.setupCacheCleanup();
  }

  // 🧹 Cache cleanup
  private setupCacheCleanup(): void {
    setInterval(() => {
      const now = Date.now();
      for (const [key, entry] of this.cache.entries()) {
        if (now - entry.timestamp > entry.ttl) {
          this.cache.delete(key);
          console.log(`🗑️ Cache entry expired: ${key}`);
        }
      }
    }, 60000); // Clean up every minute
  }

  // 🔑 Generate cache key
  private getCacheKey(method: string, endpoint: string, params?: any): string {
    const paramString = params ? JSON.stringify(params) : '';
    return `${method}:${endpoint}:${paramString}`;
  }

  // 💾 Get from cache
  private getFromCache<T>(key: string): T | null {
    const entry = this.cache.get(key);
    if (!entry) return null;

    const now = Date.now();
    if (now - entry.timestamp > entry.ttl) {
      this.cache.delete(key);
      return null;
    }

    console.log(`📋 Cache hit: ${key}`);
    return entry.data;
  }

  // 💾 Set cache
  private setCache<T>(key: string, data: T, ttl: number = this.defaultTTL): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now(),
      ttl,
    });
    console.log(`💾 Cached: ${key}`);
  }

  // 🔄 Request deduplication
  private async dedupedRequest<T>(
    key: string,
    requestFn: () => Promise<T>,
    ttl?: number
  ): Promise<T> {
    // Check cache first
    const cached = this.getFromCache<T>(key);
    if (cached) return cached;

    // Check if request is already pending
    const pending = this.pendingRequests.get(key);
    if (pending) {
      console.log(`⏳ Deduplicating request: ${key}`);
      return pending;
    }

    // Make new request
    const request = requestFn();
    this.pendingRequests.set(key, request);

    try {
      const result = await request;
      this.setCache(key, result, ttl);
      return result;
    } finally {
      this.pendingRequests.delete(key);
    }
  }

  // 👥 Cached user list
  async listUsers(params?: ListUsersParams, options?: { ttl?: number; forceRefresh?: boolean }) {
    const key = this.getCacheKey('GET', '/users', params);
    
    if (options?.forceRefresh) {
      this.cache.delete(key);
      console.log(`🔄 Force refreshing: ${key}`);
    }

    return this.dedupedRequest(
      key,
      () => this.baseClient.listUsers(params),
      options?.ttl
    );
  }

  // 👤 Cached user by ID
  async getUserById(userId: number, options?: { ttl?: number; forceRefresh?: boolean }) {
    const key = this.getCacheKey('GET', `/users/${userId}`);
    
    if (options?.forceRefresh) {
      this.cache.delete(key);
      console.log(`🔄 Force refreshing user: ${userId}`);
    }

    return this.dedupedRequest(
      key,
      () => this.baseClient.getUserById(userId),
      options?.ttl
    );
  }

  // ➕ Create user with cache invalidation
  async createUser(userData: CreateUserRequest) {
    console.log('👤 Creating user and invalidating cache...');
    
    const result = await this.baseClient.createUser(userData);
    
    // Invalidate relevant cache entries
    this.invalidateUserListCache();
    
    // Emit event for real-time updates
    this.emit('userCreated', result.data);
    
    return result;
  }

  // ✏️ Update user with cache invalidation
  async updateUser(userId: number, userData: UpdateUserRequest) {
    console.log(`📝 Updating user ${userId} and invalidating cache...`);
    
    const result = await this.baseClient.updateUser(userId, userData);
    
    // Invalidate specific user cache
    const userKey = this.getCacheKey('GET', `/users/${userId}`);
    this.cache.delete(userKey);
    
    // Invalidate user list cache
    this.invalidateUserListCache();
    
    // Emit event for real-time updates
    this.emit('userUpdated', result.data);
    
    return result;
  }

  // 🗑️ Delete user with cache invalidation
  async deleteUser(userId: number) {
    console.log(`🗑️ Deleting user ${userId} and invalidating cache...`);
    
    const result = await this.baseClient.deleteUser(userId);
    
    // Invalidate specific user cache
    const userKey = this.getCacheKey('GET', `/users/${userId}`);
    this.cache.delete(userKey);
    
    // Invalidate user list cache
    this.invalidateUserListCache();
    
    // Emit event for real-time updates
    this.emit('userDeleted', userId);
    
    return result;
  }

  // 🧹 Cache invalidation helpers
  private invalidateUserListCache(): void {
    console.log('🧹 Invalidating user list cache...');
    for (const key of this.cache.keys()) {
      if (key.startsWith('GET:/users:')) {
        this.cache.delete(key);
      }
    }
  }

  // 🗑️ Clear all cache
  clearCache(): void {
    console.log('🧹 Clearing all cache...');
    this.cache.clear();
  }

  // 📊 Cache statistics
  getCacheStats() {
    const entries = Array.from(this.cache.entries());
    const now = Date.now();
    
    return {
      totalEntries: entries.length,
      validEntries: entries.filter(([, entry]) => now - entry.timestamp <= entry.ttl).length,
      expiredEntries: entries.filter(([, entry]) => now - entry.timestamp > entry.ttl).length,
      cacheSize: JSON.stringify(Object.fromEntries(this.cache)).length,
    };
  }
}

// 🎮 Usage with real-time updates
const advancedUsageExample = async (): Promise<void> => {
  const baseClient = new UserApiClient({
    baseURL: 'https://api.example.com/v1',
    apiKey: 'your-api-key-here',
  });

  const advancedClient = new AdvancedUserApiClient(baseClient);

  // 🔔 Set up real-time event listeners
  advancedClient.on('userCreated', (user: User) => {
    console.log('🎉 New user created:', user.name);
    // Update UI, send notifications, etc.
  });

  advancedClient.on('userUpdated', (user: User) => {
    console.log('📝 User updated:', user.name);
    // Update UI with new data
  });

  advancedClient.on('userDeleted', (userId: number) => {
    console.log(`🗑️ User ${userId} deleted`);
    // Remove from UI, clean up references, etc.
  });

  try {
    // 📋 First call - hits API
    const users1 = await advancedClient.listUsers();
    console.log('📡 First call - API hit');

    // 📋 Second call - hits cache
    const users2 = await advancedClient.listUsers();
    console.log('💾 Second call - cache hit');

    // 👤 Get user with caching
    const user = await advancedClient.getUserById(123);
    console.log('👤 User (cached):', user.data.name);

    // ➕ Create user (invalidates cache)
    await advancedClient.createUser({
      email: '[email protected]',
      name: 'New User',
      role: 'user',
    });

    // 📋 List users again - cache was invalidated, so hits API
    const users3 = await advancedClient.listUsers();
    console.log('📡 After create - API hit (cache invalidated)');

    // 📊 Check cache statistics
    const stats = advancedClient.getCacheStats();
    console.log('📊 Cache stats:', stats);

  } catch (error) {
    console.error('💥 Error:', error);
  }
};

🤖 Automated Type Generation Workflows

🔄 CI/CD Integration

Let’s create automated workflows to keep our types in sync with API changes:

// 🌟 CI/CD configuration for automated type generation
// .github/workflows/api-types.yml
{
  name: 'API Type Generation',
  on: {
    push: {
      branches: ['main', 'develop'],
      paths: ['openapi.yaml', 'api-spec/**']
    },
    pull_request: {
      paths: ['openapi.yaml', 'api-spec/**']
    },
    schedule: [
      { cron: '0 6 * * *' } // Daily at 6 AM
    ]
  },
  jobs: {
    'generate-types': {
      'runs-on': 'ubuntu-latest',
      steps: [
        {
          name: 'Checkout code',
          uses: 'actions/checkout@v4'
        },
        {
          name: 'Setup Node.js',
          uses: 'actions/setup-node@v4',
          with: {
            'node-version': '18',
            cache: 'npm'
          }
        },
        {
          name: 'Install dependencies',
          run: 'npm ci'
        },
        {
          name: 'Validate OpenAPI spec',
          run: 'npm run validate:openapi'
        },
        {
          name: 'Generate TypeScript types',
          run: 'npm run generate:types'
        },
        {
          name: 'Generate API client',
          run: 'npm run generate:client'
        },
        {
          name: 'Run type checking',
          run: 'npm run typecheck'
        },
        {
          name: 'Run tests',
          run: 'npm test'
        },
        {
          name: 'Check for changes',
          run: |
            if [[ `git status --porcelain` ]]; then
              echo "::set-output name=changes::true"
              echo "Generated types have changes"
            else
              echo "::set-output name=changes::false"
              echo "No changes in generated types"
            fi
          id: 'check-changes'
        },
        {
          name: 'Commit generated types',
          if: 'steps.check-changes.outputs.changes == \'true\'',
          run: |
            git config --local user.email "[email protected]"
            git config --local user.name "GitHub Action"
            git add src/types/
            git commit -m "🤖 Update generated API types"
            git push
        }
      ]
    }
  }
}

📋 Build Scripts

Enhanced build scripts for comprehensive type generation:

// 🛠️ Advanced build scripts
// scripts/generate-types.js
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const yaml = require('js-yaml');

class TypeGenerator {
  constructor() {
    this.config = {
      specPath: 'openapi.yaml',
      outputDir: 'src/types',
      clientDir: 'src/api/generated',
      validationEnabled: true,
    };
  }

  // 🔍 Validate OpenAPI specification
  validateSpec() {
    console.log('🔍 Validating OpenAPI specification...');
    
    try {
      const specContent = fs.readFileSync(this.config.specPath, 'utf8');
      const spec = yaml.load(specContent);
      
      // Basic validation checks
      if (!spec.openapi) {
        throw new Error('Missing OpenAPI version');
      }
      
      if (!spec.info) {
        throw new Error('Missing API info');
      }
      
      if (!spec.paths) {
        throw new Error('Missing API paths');
      }
      
      console.log('✅ OpenAPI specification is valid');
      console.log(`📋 API: ${spec.info.title} v${spec.info.version}`);
      console.log(`🛣️ Endpoints: ${Object.keys(spec.paths).length}`);
      
      return spec;
    } catch (error) {
      console.error('❌ OpenAPI validation failed:', error.message);
      process.exit(1);
    }
  }

  // 🎯 Generate TypeScript types
  generateTypes() {
    console.log('🎯 Generating TypeScript types...');
    
    try {
      // Ensure output directory exists
      if (!fs.existsSync(this.config.outputDir)) {
        fs.mkdirSync(this.config.outputDir, { recursive: true });
      }
      
      // Generate types using openapi-typescript
      execSync(`npx openapi-typescript ${this.config.specPath} --output ${this.config.outputDir}/api.ts`, {
        stdio: 'inherit'
      });
      
      // Generate additional utility types
      this.generateUtilityTypes();
      
      console.log('✅ TypeScript types generated successfully');
    } catch (error) {
      console.error('❌ Type generation failed:', error.message);
      process.exit(1);
    }
  }

  // 🔧 Generate utility types
  generateUtilityTypes() {
    console.log('🔧 Generating utility types...');
    
    const utilityTypes = `
// 🤖 Auto-generated utility types
// DO NOT EDIT MANUALLY - This file is generated automatically

import type { components, operations } from './api';

// 📦 Schema types
export type User = components['schemas']['User'];
export type CreateUserRequest = components['schemas']['CreateUserRequest'];
export type UpdateUserRequest = components['schemas']['UpdateUserRequest'];
export type ApiResponse<T = any> = components['schemas']['ApiResponse'] & { data?: T };
export type PaginatedResponse<T = any> = components['schemas']['PaginatedResponse'] & { data: T[] };

// 📤 Operation types
export type ListUsersParams = operations['listUsers']['parameters']['query'];
export type ListUsersResponse = operations['listUsers']['responses']['200']['content']['application/json'];
export type CreateUserParams = operations['createUser']['requestBody']['content']['application/json'];
export type CreateUserResponse = operations['createUser']['responses']['201']['content']['application/json'];
export type GetUserParams = operations['getUserById']['parameters']['path'];
export type GetUserResponse = operations['getUserById']['responses']['200']['content']['application/json'];
export type UpdateUserParams = operations['updateUser']['parameters']['path'] & {
  body: operations['updateUser']['requestBody']['content']['application/json'];
};
export type UpdateUserResponse = operations['updateUser']['responses']['200']['content']['application/json'];
export type DeleteUserParams = operations['deleteUser']['parameters']['path'];
export type DeleteUserResponse = operations['deleteUser']['responses']['200']['content']['application/json'];

// 🎯 Helper types
export type UserRole = User['role'];
export type UserId = User['id'];
export type UserEmail = User['email'];

// 🔍 Query helpers
export type UserSearchFilters = Pick<ListUsersParams, 'role' | 'isActive'>;
export type UserSortableFields = keyof Pick<User, 'name' | 'email' | 'createdAt' | 'updatedAt'>;

// 📋 Form types
export type UserCreateForm = Omit<CreateUserRequest, 'metadata'> & {
  metadata?: Record<string, string>;
};
export type UserUpdateForm = Partial<UserCreateForm>;

// 🛡️ Type guards
export const isValidUserRole = (role: string): role is UserRole => {
  return ['admin', 'user', 'moderator'].includes(role);
};

export const isUser = (obj: any): obj is User => {
  return obj && 
    typeof obj.id === 'number' &&
    typeof obj.email === 'string' &&
    typeof obj.name === 'string';
};

export const isApiResponse = <T>(obj: any): obj is ApiResponse<T> => {
  return obj &&
    typeof obj.success === 'boolean' &&
    typeof obj.message === 'string';
};

// 📊 Utility functions
export const getUserDisplayName = (user: User): string => {
  return user.name || user.email;
};

export const isActiveUser = (user: User): boolean => {
  return user.isActive !== false;
};

export const getUserRoleBadgeColor = (role: UserRole): string => {
  switch (role) {
    case 'admin': return 'red';
    case 'moderator': return 'orange';
    case 'user': return 'blue';
    default: return 'gray';
  }
};

// 📈 Constants
export const USER_ROLES = ['admin', 'user', 'moderator'] as const;
export const MAX_USERS_PER_PAGE = 100;
export const DEFAULT_PAGE_SIZE = 20;

// 🎨 Generated at: ${new Date().toISOString()}
`;

    fs.writeFileSync(
      path.join(this.config.outputDir, 'utils.ts'),
      utilityTypes.trim()
    );
    
    console.log('✅ Utility types generated');
  }

  // 🚀 Generate API client
  generateClient() {
    console.log('🚀 Generating API client...');
    
    try {
      // Ensure output directory exists
      if (!fs.existsSync(this.config.clientDir)) {
        fs.mkdirSync(this.config.clientDir, { recursive: true });
      }
      
      // Generate client using openapi-typescript-codegen
      execSync(`npx openapi-codegen --input ${this.config.specPath} --output ${this.config.clientDir}`, {
        stdio: 'inherit'
      });
      
      console.log('✅ API client generated successfully');
    } catch (error) {
      console.error('❌ Client generation failed:', error.message);
      process.exit(1);
    }
  }

  // 🔍 Validate generated types
  validateGeneratedTypes() {
    console.log('🔍 Validating generated types...');
    
    try {
      // Run TypeScript compiler to check for type errors
      execSync('npx tsc --noEmit --project tsconfig.json', {
        stdio: 'inherit'
      });
      
      console.log('✅ Generated types are valid');
    } catch (error) {
      console.error('❌ Type validation failed');
      process.exit(1);
    }
  }

  // 🎯 Run full generation pipeline
  async run() {
    console.log('🚀 Starting API type generation pipeline...\n');
    
    const startTime = Date.now();
    
    try {
      // Step 1: Validate specification
      this.validateSpec();
      console.log();
      
      // Step 2: Generate TypeScript types
      this.generateTypes();
      console.log();
      
      // Step 3: Generate API client
      this.generateClient();
      console.log();
      
      // Step 4: Validate generated code
      this.validateGeneratedTypes();
      console.log();
      
      const endTime = Date.now();
      console.log(`🎉 Type generation completed successfully in ${endTime - startTime}ms`);
      
    } catch (error) {
      console.error('💥 Type generation pipeline failed:', error);
      process.exit(1);
    }
  }
}

// 🎮 Run the generator
if (require.main === module) {
  const generator = new TypeGenerator();
  generator.run();
}

module.exports = TypeGenerator;

🔄 Watch Mode for Development

Development setup with automatic type regeneration:

// 🛠️ Development watch script
// scripts/watch-types.js
const fs = require('fs');
const chokidar = require('chokidar');
const TypeGenerator = require('./generate-types');

class TypeWatcher {
  constructor() {
    this.generator = new TypeGenerator();
    this.isGenerating = false;
    this.pendingGeneration = false;
  }

  // 🔍 Start watching for changes
  start() {
    console.log('👀 Starting type generation watcher...');
    console.log('📁 Watching: openapi.yaml, api-spec/**');
    console.log('🔄 Auto-regenerating types on changes...\n');

    const watcher = chokidar.watch(['openapi.yaml', 'api-spec/**'], {
      persistent: true,
      ignoreInitial: false,
    });

    watcher.on('change', (path) => {
      console.log(`📝 File changed: ${path}`);
      this.scheduleGeneration();
    });

    watcher.on('add', (path) => {
      console.log(`➕ File added: ${path}`);
      this.scheduleGeneration();
    });

    watcher.on('unlink', (path) => {
      console.log(`🗑️ File removed: ${path}`);
      this.scheduleGeneration();
    });

    watcher.on('error', (error) => {
      console.error('👀 Watcher error:', error);
    });

    console.log('✅ Type watcher started. Press Ctrl+C to stop.\n');

    // Handle graceful shutdown
    process.on('SIGINT', () => {
      console.log('\n🛑 Shutting down type watcher...');
      watcher.close();
      process.exit(0);
    });
  }

  // ⏰ Schedule type generation with debouncing
  scheduleGeneration() {
    if (this.isGenerating) {
      this.pendingGeneration = true;
      console.log('⏳ Generation in progress, queuing...');
      return;
    }

    // Debounce rapid changes
    clearTimeout(this.generationTimer);
    this.generationTimer = setTimeout(() => {
      this.runGeneration();
    }, 1000);
  }

  // 🎯 Run type generation
  async runGeneration() {
    if (this.isGenerating) return;

    this.isGenerating = true;
    this.pendingGeneration = false;

    try {
      console.log('🔄 Regenerating types...');
      const startTime = Date.now();

      await this.generator.run();

      const endTime = Date.now();
      console.log(`✅ Types regenerated in ${endTime - startTime}ms\n`);

      // Check if another generation is pending
      if (this.pendingGeneration) {
        console.log('🔄 Running queued generation...');
        this.isGenerating = false;
        this.scheduleGeneration();
      } else {
        this.isGenerating = false;
        console.log('👀 Watching for changes...\n');
      }

    } catch (error) {
      console.error('❌ Type generation failed:', error);
      this.isGenerating = false;
    }
  }
}

// 🎮 Start the watcher
if (require.main === module) {
  const watcher = new TypeWatcher();
  watcher.start();
}

module.exports = TypeWatcher;

🧪 Testing Generated Types

🎯 Type-Safe Testing Patterns

Let’s create comprehensive tests for our generated types and API client:

// 🧪 Tests for generated types and API client
// src/__tests__/api-client.test.ts
import { describe, test, expect, beforeEach, jest } from '@jest/globals';
import axios from 'axios';
import { UserApiClient, AdvancedUserApiClient } from '../api/client';
import type {
  User,
  CreateUserRequest,
  UpdateUserRequest,
  ListUsersResponse,
  CreateUserResponse,
} from '../types/api';

// 🎭 Mock axios
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('UserApiClient', () => {
  let client: UserApiClient;
  let mockAxiosInstance: jest.Mocked<any>;

  beforeEach(() => {
    // 🏗️ Setup mock axios instance
    mockAxiosInstance = {
      request: jest.fn(),
      interceptors: {
        request: { use: jest.fn() },
        response: { use: jest.fn() },
      },
    };

    mockedAxios.create.mockReturnValue(mockAxiosInstance);

    client = new UserApiClient({
      baseURL: 'https://api.test.com',
      apiKey: 'test-key',
    });
  });

  describe('listUsers', () => {
    test('should fetch users with correct parameters', async () => {
      // 📋 Mock response data
      const mockResponse: ListUsersResponse = {
        success: true,
        message: 'Users retrieved successfully',
        data: {
          data: [
            {
              id: 1,
              email: '[email protected]',
              name: 'Test User',
              role: 'user',
              isActive: true,
              createdAt: '2023-01-01T00:00:00Z',
              updatedAt: '2023-01-01T00:00:00Z',
            },
          ],
          pagination: {
            page: 1,
            limit: 20,
            total: 1,
            pages: 1,
          },
        },
      };

      mockAxiosInstance.request.mockResolvedValue({ data: mockResponse });

      // 🎯 Test the method
      const result = await client.listUsers({ page: 1, limit: 20 });

      // ✅ Verify the call
      expect(mockAxiosInstance.request).toHaveBeenCalledWith({
        method: 'GET',
        url: '/users',
        params: { page: 1, limit: 20 },
      });

      // ✅ Verify the response
      expect(result).toEqual(mockResponse);
      expect(result.data.data).toHaveLength(1);
      expect(result.data.data[0]).toMatchObject({
        id: 1,
        email: '[email protected]',
        name: 'Test User',
      });
    });

    test('should handle API errors correctly', async () => {
      // 💥 Mock error response
      const errorResponse = {
        response: {
          data: { message: 'Validation failed' },
          status: 400,
        },
      };

      mockAxiosInstance.request.mockRejectedValue(errorResponse);

      // 🎯 Test error handling
      await expect(client.listUsers()).rejects.toThrow('API Error: Validation failed');
    });
  });

  describe('createUser', () => {
    test('should create user with type-safe data', async () => {
      // 👤 Type-safe user data
      const userData: CreateUserRequest = {
        email: '[email protected]',
        name: 'New User',
        age: 25,
        role: 'user',
        metadata: {
          department: 'Engineering',
        },
      };

      const mockResponse: CreateUserResponse = {
        success: true,
        message: 'User created successfully',
        data: {
          id: 123,
          ...userData,
          isActive: true,
          createdAt: '2023-01-01T00:00:00Z',
          updatedAt: '2023-01-01T00:00:00Z',
        },
      };

      mockAxiosInstance.request.mockResolvedValue({ data: mockResponse });

      // 🎯 Test user creation
      const result = await client.createUser(userData);

      // ✅ Verify the call
      expect(mockAxiosInstance.request).toHaveBeenCalledWith({
        method: 'POST',
        url: '/users',
        data: userData,
      });

      // ✅ Verify the response
      expect(result.data.email).toBe(userData.email);
      expect(result.data.name).toBe(userData.name);
      expect(result.data.id).toBe(123);
    });

    test('should enforce type safety at compile time', () => {
      // 🎯 These should cause TypeScript errors if uncommented:
      
      // ❌ Missing required fields
      // const invalidData1: CreateUserRequest = {
      //   name: 'Test User' // Missing email
      // };

      // ❌ Invalid role value
      // const invalidData2: CreateUserRequest = {
      //   email: '[email protected]',
      //   name: 'Test User',
      //   role: 'invalid-role' // Type error
      // };

      // ❌ Invalid metadata type
      // const invalidData3: CreateUserRequest = {
      //   email: '[email protected]',
      //   name: 'Test User',
      //   metadata: 'invalid' // Should be object
      // };

      // ✅ Valid data
      const validData: CreateUserRequest = {
        email: '[email protected]',
        name: 'Test User',
        role: 'user',
        metadata: { key: 'value' },
      };

      expect(validData.email).toBe('[email protected]');
    });
  });
});

describe('AdvancedUserApiClient', () => {
  let baseClient: UserApiClient;
  let advancedClient: AdvancedUserApiClient;

  beforeEach(() => {
    baseClient = new UserApiClient({
      baseURL: 'https://api.test.com',
      apiKey: 'test-key',
    });

    advancedClient = new AdvancedUserApiClient(baseClient);
  });

  describe('caching', () => {
    test('should cache API responses', async () => {
      // 📋 Mock response
      const mockUser: User = {
        id: 1,
        email: '[email protected]',
        name: 'Cached User',
        role: 'user',
        isActive: true,
        createdAt: '2023-01-01T00:00:00Z',
        updatedAt: '2023-01-01T00:00:00Z',
      };

      const mockResponse = {
        success: true,
        message: 'User retrieved',
        data: mockUser,
      };

      // 🎭 Mock base client method
      jest.spyOn(baseClient, 'getUserById').mockResolvedValue(mockResponse);

      // 🎯 First call - should hit API
      const result1 = await advancedClient.getUserById(1);
      expect(baseClient.getUserById).toHaveBeenCalledTimes(1);

      // 🎯 Second call - should use cache
      const result2 = await advancedClient.getUserById(1);
      expect(baseClient.getUserById).toHaveBeenCalledTimes(1); // Still only called once

      expect(result1).toEqual(result2);
    });

    test('should invalidate cache on user update', async () => {
      // 🎭 Mock methods
      jest.spyOn(baseClient, 'getUserById').mockResolvedValue({
        success: true,
        message: 'User retrieved',
        data: { id: 1, email: '[email protected]', name: 'Test' } as User,
      });

      jest.spyOn(baseClient, 'updateUser').mockResolvedValue({
        success: true,
        message: 'User updated',
        data: { id: 1, email: '[email protected]', name: 'Updated' } as User,
      });

      // 🎯 Get user - caches result
      await advancedClient.getUserById(1);
      expect(baseClient.getUserById).toHaveBeenCalledTimes(1);

      // 🎯 Update user - should invalidate cache
      await advancedClient.updateUser(1, { name: 'Updated Name' });

      // 🎯 Get user again - should hit API due to cache invalidation
      await advancedClient.getUserById(1);
      expect(baseClient.getUserById).toHaveBeenCalledTimes(2);
    });
  });

  describe('real-time events', () => {
    test('should emit events on user operations', async () => {
      // 🎭 Mock event listeners
      const userCreatedListener = jest.fn();
      const userUpdatedListener = jest.fn();
      const userDeletedListener = jest.fn();

      advancedClient.on('userCreated', userCreatedListener);
      advancedClient.on('userUpdated', userUpdatedListener);
      advancedClient.on('userDeleted', userDeletedListener);

      // 🎭 Mock base client methods
      const mockUser: User = {
        id: 123,
        email: '[email protected]',
        name: 'Event User',
        role: 'user',
        isActive: true,
        createdAt: '2023-01-01T00:00:00Z',
        updatedAt: '2023-01-01T00:00:00Z',
      };

      jest.spyOn(baseClient, 'createUser').mockResolvedValue({
        success: true,
        message: 'User created',
        data: mockUser,
      });

      jest.spyOn(baseClient, 'updateUser').mockResolvedValue({
        success: true,
        message: 'User updated',
        data: { ...mockUser, name: 'Updated User' },
      });

      jest.spyOn(baseClient, 'deleteUser').mockResolvedValue({
        success: true,
        message: 'User deleted',
      });

      // 🎯 Test create event
      await advancedClient.createUser({
        email: '[email protected]',
        name: 'Event User',
      });
      expect(userCreatedListener).toHaveBeenCalledWith(mockUser);

      // 🎯 Test update event
      await advancedClient.updateUser(123, { name: 'Updated User' });
      expect(userUpdatedListener).toHaveBeenCalledWith({
        ...mockUser,
        name: 'Updated User',
      });

      // 🎯 Test delete event
      await advancedClient.deleteUser(123);
      expect(userDeletedListener).toHaveBeenCalledWith(123);
    });
  });
});

// 🎯 Type-level tests using TypeScript's type system
describe('Type Safety', () => {
  test('should ensure correct types are used', () => {
    // ✅ These demonstrate compile-time type safety
    
    // User type should have all required properties
    const user: User = {
      id: 1,
      email: '[email protected]',
      name: 'Test User',
      // Optional properties can be omitted
    };

    // CreateUserRequest should not include generated fields
    const createRequest: CreateUserRequest = {
      email: '[email protected]',
      name: 'New User',
      // id, createdAt, updatedAt should not be allowed here
    };

    // UpdateUserRequest should make all fields optional
    const updateRequest: UpdateUserRequest = {
      name: 'Updated Name',
      // All other fields are optional
    };

    expect(user.id).toBe(1);
    expect(createRequest.email).toBe('[email protected]');
    expect(updateRequest.name).toBe('Updated Name');
  });
});

🎉 Conclusion

Congratulations! You’ve mastered the art of generating TypeScript types from OpenAPI specifications! 🔧

🎯 What You’ve Learned

  • 📋 OpenAPI Specifications: Creating comprehensive API documentation as code
  • 🤖 Automated Type Generation: Tools and workflows for type synchronization
  • 🏗️ Type-Safe API Clients: Building robust HTTP clients with full type safety
  • ⚡ Advanced Features: Caching, deduplication, and real-time updates
  • 🔄 CI/CD Integration: Automated workflows for continuous type synchronization
  • 🧪 Testing Strategies: Comprehensive testing for generated types and clients

🚀 Key Benefits

  • 🛡️ Type Safety: Eliminate runtime errors with compile-time type checking
  • 🔄 Synchronization: Keep frontend and backend types automatically in sync
  • 📚 Documentation: Living documentation that’s always up-to-date
  • ⚡ Developer Experience: IntelliSense, autocomplete, and instant feedback
  • 🧹 Maintainability: Reduce manual type maintenance and human errors

🔥 Best Practices Recap

  1. 📋 Single Source of Truth: Use OpenAPI specs as the authoritative API contract
  2. 🤖 Automation: Automate type generation in development and CI/CD pipelines
  3. 🧪 Validation: Always validate generated types with TypeScript compiler
  4. 📦 Versioning: Version your API specs and generated types together
  5. 🔍 Monitoring: Set up alerts for type generation failures and schema changes

You’re now equipped to build enterprise-grade, type-safe REST API integrations that scale with your applications and stay perfectly synchronized with your backend services! 🌟

Happy coding, and may your APIs always be perfectly typed! 🎯✨