hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses API documentation with OpenAPI and @hectoday/http

Why documentation

  • Undocumented APIs are unusable
  • The OpenAPI standard
  • Project setup

Describing your API

  • Paths and operations
  • Request parameters
  • Request bodies
  • Responses

Schemas and reuse

  • Component schemas
  • Zod to OpenAPI
  • Error schemas

Beyond endpoints

  • Authentication documentation
  • Tags and grouping
  • Examples and descriptions

Serving and consuming

  • Serving the spec
  • Interactive docs with Scalar
  • Generating client SDKs

Wrapping up

  • Versioned specs
  • Checklist

Generating client SDKs

Interactive docs help developers explore your API. But when they sit down to write actual code, they want something different: types, autocompletion, and compile-time checks. They don’t want to copy a URL from Swagger UI, manually type out the response shape, and hope they got it right. They want the compiler to catch their mistakes.

The OpenAPI spec makes this possible. Because the spec describes every path, parameter, request body, and response in a structured format, tools can read it and generate a fully typed TypeScript client. Let’s see how.

Generating types from the spec

The openapi-typescript tool reads an OpenAPI spec and generates TypeScript type definitions:

npx openapi-typescript http://localhost:3000/openapi.json -o src/api-types.ts

This fetches the spec from your running server and writes type definitions to src/api-types.ts. The generated file looks something like this:

// Generated: src/api-types.ts
export interface components {
  schemas: {
    Book: {
      id: string;
      title: string;
      author: { id: string; name: string };
      genre: "fiction" | "science-fiction" | "fantasy" | "non-fiction" | "other";
      ratings: { average: number | null; count: number };
      createdAt: string;
    };
    CreateBook: {
      title: string;
      authorId: string;
      genre: "fiction" | "science-fiction" | "fantasy" | "non-fiction" | "other";
      description?: string;
    };
    // ... more schemas
  };
}

export interface paths {
  "/v2/books": {
    get: {
      responses: {
        200: {
          content: {
            "application/json": {
              data: components["schemas"]["Book"][];
              meta: components["schemas"]["PaginationMeta"];
            };
          };
        };
      };
    };
    post: {
      requestBody: { content: { "application/json": components["schemas"]["CreateBook"] } };
      responses: {
        201: { content: { "application/json": components["schemas"]["Book"] } };
      };
    };
  };
}

Look at what you get. Every schema from the spec becomes a TypeScript interface. Every path becomes a typed entry. The Book type has genre as a union of literal strings, ratings.average as number | null, and description as optional (?). All of that came from the spec, which came from the Zod schemas. No manual type definitions needed.

Making type-safe API calls

Types alone don’t make API calls. The openapi-fetch library uses the generated types to provide type-safe fetch calls:

import createClient from "openapi-fetch";
import type { paths } from "./api-types.js";

const client = createClient<paths>({ baseUrl: "https://api.example.com" });

// Type-safe: TypeScript knows the response shape
const { data, error } = await client.GET("/v2/books", {
  params: { query: { genre: "fiction", page: 1 } },
});

if (data) {
  // data.data is Book[], fully typed
  data.data.forEach((book) => console.log(book.title));
}

// Type-safe: TypeScript knows what fields to send
const { data: created } = await client.POST("/v2/books", {
  body: {
    title: "Kindred",
    authorId: "550e8400-...",
    genre: "science-fiction",
  },
});

What happens if you make a typo in a field name? The compiler catches it. What if you forget a required field? The compiler catches it. What if you use an invalid enum value like "sci-fi" instead of "science-fiction"? The compiler catches it. All at compile time, before you even run the code.

This is a huge deal for frontend teams. Instead of discovering API mistakes at runtime (“why is the response undefined?”), they catch them while writing the code.

The full chain

Take a step back and look at what we’ve built across this course:

Zod schema → validates requests at runtime
         ↓
OpenAPI spec → generated from Zod
         ↓
TypeScript types → generated from OpenAPI
         ↓
API client → type-safe fetch calls

One Zod schema produces four things: runtime validation, API documentation, TypeScript types, and a type-safe client. Change the schema and the entire chain updates. Add a field to the Zod schema, regenerate the types, and the compiler tells every frontend call site that a new field is available (or that a removed field is no longer valid).

When to use generated clients

Internal frontends. Your SPA or mobile app talks to your API. Generated types ensure the frontend matches the backend. If the backend adds a required field, the frontend gets a compile error until it sends the field.

Partner integrations. Share the OpenAPI spec with partners. They generate a typed client in whatever language they use (TypeScript, Python, Go, Java) and get the same type safety.

Microservices. Service A calls service B. Generated types ensure the caller’s request matches what the callee expects.

This is exactly how production applications handle API integration. Instead of manually maintaining type definitions that can drift from the actual API, they generate them from a single source of truth.

In the final section, we will look at how to handle versioned specs when your API evolves.

Exercises

Exercise 1: Generate TypeScript types from your OpenAPI spec using openapi-typescript. Open the generated file and inspect the output.

Exercise 2: Use openapi-fetch to make type-safe API calls. Verify that autocompletion works for paths, parameters, and response types.

Exercise 3: Change a field name in the Zod schema. Regenerate the spec and types. Verify that the client gets a compile error for the old name.

What is the benefit of generating a TypeScript client from the OpenAPI spec?

← Interactive docs with Scalar Versioned specs →

© 2026 hectoday. All rights reserved.