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

Versioned specs

As your API evolves, you may end up with multiple versions running at the same time. Maybe v1 is still in production for existing clients while v2 introduces a new response format. Both versions need their own spec and their own docs, because a client using v1 should not be reading v2 documentation.

@hectoday/openapi makes this straightforward. You create a separate openapi() instance for each version.

Separate specs per version

Each version gets its own routes array, its own openapi() call, and its own spec and docs paths:

Code along
const v1Api = openapi(v1Routes, {
  info: { title: "Book Catalog API", version: "1.0.0" },
  specPath: "/v1/openapi.json",
  docsPath: "/v1/docs",
});

const v2Api = openapi(v2Routes, {
  info: { title: "Book Catalog API", version: "2.0.0" },
  specPath: "/v2/openapi.json",
  docsPath: "/v2/docs",
});

const app = setup({
  routes: [
    ...v1Routes,
    ...v2Routes,
    v1Api.spec(route),
    v1Api.docs(route),
    v2Api.spec(route),
    v2Api.docs(route),
  ],
});

Now your API serves four documentation endpoints:

  • /v1/openapi.json and /v1/docs for v1 clients
  • /v2/openapi.json and /v2/docs for v2 clients

Each spec only describes the routes for its version. A client integrating with v1 sees v1 endpoints, v1 schemas, and v1 response shapes. They are not confused by v2 changes that do not apply to them.

When to version

Not every change needs a new version. Adding a new endpoint or a new optional field to a response is backwards-compatible. Existing clients are not affected. You can add those to the current version.

A new version is warranted when you make a breaking change: renaming a field, removing an endpoint, changing a required field’s type, or restructuring a response. These changes would break existing clients, so you keep the old version running while new clients adopt the new one.

The version number in the info object should match the version in your URL paths. If your routes are under /v2/books, the spec should say version: "2.0.0". This keeps everything consistent.

Generating typed clients per version

When you have versioned specs, client generation works the same way. You just point the tool at the right spec:

# Generate types for v1 clients
npx openapi-typescript http://localhost:3000/v1/openapi.json -o src/v1-types.ts

# Generate types for v2 clients
npx openapi-typescript http://localhost:3000/v2/openapi.json -o src/v2-types.ts

Each version gets its own type file. Frontend teams choose the version that matches the API they are calling.

In the next and final lesson, we will put everything together with a checklist and the complete documented book catalog.

Exercises

Exercise 1: Create a minimal v1 routes array with a single endpoint (like GET /v1/books that returns a simpler schema). Set up a separate openapi() instance with specPath: "/v1/openapi.json". Verify both v1 and v2 specs are served at their respective paths.

Exercise 2: Open /v1/docs and /v2/docs side by side. Verify each one only shows its own version’s endpoints.

Exercise 3: Generate typed clients from both specs. Verify the v1 types are different from the v2 types.

When should you create a new API version instead of updating the existing one?

← Generating client SDKs Checklist →

© 2026 hectoday. All rights reserved.