openapi-typescript

website·Indexed 34 days ago

https://openapi-ts.dev/

A toolset for turning OpenAPI 3.0 & 3.1 schemas into static TypeScript types. The ecosystem includes a CLI for schema transformation, openapi-fetch for a type-safe fetch client, and openapi-react-query, a type-safe wrapper around @tanstack/react-query.

Tokens
18K
Snippets
94
Records
114
Agent score
70%

What's inside openapi-typescript

  1. Overview of openapi-react-query

    openapi-react-query is a lightweight (1 kb), type-safe wrapper around @tanstack/react-query designed for use with OpenAPI schemas. By integrating openapi-fetch and openapi-typescript, it ensures that URLs, parameters, request bodies, and responses are 100% type-checked against the schema, eliminating the need for manual typing or unsafe type overrides like any and as.
  2. Overview of openapi-typescript

    openapi-typescript is a Node.js tool that converts OpenAPI 3.0 and 3.1 schemas (YAML or JSON) into runtime-free TypeScript types. It supports advanced features like discriminators and is designed to handle large schemas quickly without requiring Java or node-gyp.
  3. Overview of openapi-typescript

    openapi-typescript is a Node.js-based tool that converts OpenAPI 3.0 and 3.1 schemas (JSON or YAML) into TypeScript types. It generates runtime-free types quickly without requiring Java, node-gyp, or a running OpenAPI server. It supports advanced features like discriminators and can handle very large schemas in milliseconds.
  4. Install and set up openapi-react-query

    To use openapi-react-query, you must install the library along with openapi-fetch and the openapi-typescript generator. It is highly recommended to enable noUncheckedIndexedAccess in your tsconfig.json for better type safety.
    # Install runtime dependencies
    npm i openapi-react-query openapi-fetch
    
    # Install development dependencies
    npm i -D openapi-typescript typescript
    
    # Generate TypeScript types from your OpenAPI schema
    npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts
  5. Fetch data using the $api.useQuery hook

    The $api.useQuery method is a type-safe wrapper around the original TanStack Query useQuery hook. It ensures that the data and error returned are fully typed based on the OpenAPI schema.

    Key characteristics:

    • The query key is automatically generated as [method, path, params] to ensure unique caching per request.
    • It requires the HTTP method and the API path as the first two arguments.
    • Fetch options (like path or query parameters) are passed as the third argument.
    • Standard TanStack Query options can be passed as the fourth argument.
    import { $api } from "./api";
    
    export const App = () => {
      const { data, error, isLoading } = $api.useQuery(
        "get", 
        "/users/{user_id}", 
        {
          params: {
            path: { user_id: 5 },
          },
        }
      );
    
      if (!data || isLoading) return "Loading...";
      if (error) return `An error occured: ${error.message}`;
    
      return <div>{data.firstname}</div>;
    };
  6. Install and configure openapi-typescript

    To use openapi-typescript, you need Node.js (version 20.x or higher recommended). Install the library and TypeScript as development dependencies. To ensure types load correctly, configure module and moduleResolution in your tsconfig.json. It is also highly recommended to enable noUncheckedIndexedAccess for improved type safety.
    npm i -D openapi-typescript typescript
    // tsconfig.json
    {
      "compilerOptions": {
        "module": "ESNext", // or "NodeNext"
        "moduleResolution": "Bundler", // or "NodeNext"
        "noUncheckedIndexedAccess": true
      }
    }
  7. Customize TypeScript types using transform and postTransform

    Use transform() to modify OpenAPI nodes before they are converted to TypeScript, or postTransform() to modify them after conversion. This is useful for mapping non-standard OpenAPI formats (like date-time or binary) to specific TypeScript types (like Date or Blob).

    The metadata parameter provides context:

    • metadata.path: A $ref URI string pointing to the current schema object.
    • metadata.schema: The schema object being transformed (available only in postTransform).
    • metadata.ctx: The GlobalContext object.
    const types = await openapiTS(mySchema, {
      transform(schemaObject, metadata): string {
        // Example: Map 'date-time' format to Date type
        if ("format" in schemaObject && schemaObject.format === "date-time") {
          return schemaObject.nullable ? "Date | null" : "Date";
        }
        
        // Example: Map 'binary' format to Blob type
        if ("format" in schemaObject && schemaObject.format === "binary") {
          return schemaObject.nullable ? "Blob | null" : "Blob";
        }
      },
    });
  8. Type-safe API mocking with MSW and openapi-msw

    To prevent API mocks from becoming out-of-date when an OpenAPI specification changes, use the openapi-msw wrapper. This allows you to maintain the same level of type confidence in your mocks as you do with your API client (e.g., when using openapi-fetch).
    npm install openapi-msw
  9. Add runtime validation to Hono with openapi-ts-router

    Use openapi-ts-router to wrap a Hono router. This provides compile-time type safety for routes and methods, and integrates with Zod or Valibot for runtime validation of request parameters and bodies.
    import { Hono } from 'hono';
    import { createHonoOpenApiRouter } from 'openapi-ts-router';
    import { zValidator } from 'validation-adapters/zod';
    import * as z from 'zod';
    import { paths } from './gen/v1';
    
    export const router = new Hono();
    export const openApiRouter = createHonoOpenApiRouter<paths>(router);
    
    // GET /pet/{petId} with path validation
    openApiRouter.get('/pet/{petId}', {
      pathValidator: zValidator(z.object({ petId: z.number() })),
      handler: (c) => {
        const { petId } = c.req.valid('param');
        return c.json({ name: 'Falko', photoUrls: [] }); 
      }
    });
    
    // POST /pet with body validation
    openApiRouter.post('/pet', {
      bodyValidator: zValidator(z.object({ name: z.string(), photoUrls: z.array(z.string()) })),
      handler: (c) => {
        const { name, photoUrls } = c.req.valid('json');
        return c.json({ name, photoUrls }); 
      }
    });
  10. Implement discriminated unions using oneOf

    TypeScript unions do not natively provide XOR behavior. To ensure TypeScript can correctly discriminate between types in a oneOf composition, avoid mixing oneOf with other properties (like a shared type enum) in the same object. Instead, use oneOf by itself and move shared properties into a separate schema that the individual options inherit via allOf.

    This approach results in simpler generated types and allows TypeScript to use the type property as a discriminator.

    # ✅ Recommended Pattern
    Pet:
      oneOf:
        - $ref: "#/components/schemas/Cat"
        - $ref: "#/components/schemas/Dog"
    
    PetCommonProperties:
      type: object
      properties:
        name:
          type: string
    
    Cat:
      allOf:
        - "$ref": "#/components/schemas/PetCommonProperties"
      properties:
        type:
          type: string
          enum: [cat]
  11. Create type-safe TanStack Query options with $api.queryOptions

    The $api.queryOptions method generates type-safe Query Options that can be used with any TanStack Query API that accepts options, such as useQuery, useQueries, usePrefetchQuery, and QueryClient.fetchQuery. This is particularly useful for using query APIs not explicitly wrapped by openapi-react-query.

    The returned object includes a queryKey formatted as [method, path, params] and a queryFn pre-configured as a fetcher function, ensuring that data and error types are correctly inferred from the OpenAPI schema.

    import { useQuery } from '@tanstack/react-query';
    import { $api } from "./api";
    
    export const App = () => {
      const { data, error, isLoading } = useQuery(
        $api.queryOptions("get", "/users/{user_id}", {
          params: {
            path: { user_id: 5 },
          },
        }),
      );
    
      if (!data || isLoading) return "Loading...";
      if (error) return `An error occured: ${error.message}`;
    
      return <div>{data.firstname}</div>;
    };
  12. Enable dynamic path lookups with --path-params-as-types

    By default, TypeScript lookups on the paths object must exactly match the schema URL (e.g., /user/{user_id}). Enabling the --path-params-as-types flag allows you to use dynamic string variables for lookups, which the CLI will automatically map to the corresponding schema path.
    import type { paths } from "./my-openapi-3-schema";
    
    const url = `/user/${id}`;
    // With --path-params-as-types, this automatically matches paths['/user/{user_id}']
    type UserResponses = paths[url]["responses"];