openapi-typescript + openapi-fetch

SkillDev tools

Type-safe OpenAPI consumption in TypeScript using openapi-typescript and openapi-fetch.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the openapi-typescript + openapi-fetch skill

What this skill tells your AI

The instructions your AI receives, as published by getopenpost/openpost in .agents/skills/openapi-typescript/SKILL.md and read by ahel’s review.

Type-safe OpenAPI consumption in TypeScript. Generates runtime-free types from OpenAPI specs and provides a typed fetch client.

Type Generation

# Generate types from local spec
npx openapi-typescript openapi.json -o src/lib/api/types.d.ts

# Generate from running server
npx openapi-typescript http://localhost:8080/openapi.json -o src/lib/api/types.d.ts

# Check if types are up-to-date (CI)
npx openapi-typescript openapi.json -o src/lib/api/types.d.ts --check

openapi-fetch Client

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

const client = createClient<paths>({ baseUrl: "/api/v1" });

// GET with query params
const { data, error } = await client.GET("/posts", {
  params: { query: { workspace_id: "123" } },
});

// POST with body
const { data, error } = await client.POST("/posts", {
  body: { workspace_id: "123", content: "Hello", social_account_ids: [] },
});

// Path params
const { data } = await client.GET("/accounts/{platform}/auth-url", {
  params: {
    path: { platform: "x" },
    query: { workspace_id: "123" },
  },
});

Auth Middleware

import createClient, { type Middleware } from "openapi-fetch";

const client = createClient<paths>({ baseUrl: "/api/v1" });

client.use({
  async onRequest({ request }) {
    const token = localStorage.getItem("token");
    if (token) {
      request.headers.set("Authorization", `Bearer ${token}`);
    }
    return request;
  },
});

Re-exporting Schema Types

// In client.ts
import type { paths, components } from "./types";

export type User = components["schemas"]["UserProfile"];
export type Workspace = components["schemas"]["WorkspaceResp"];
export type Post = components["schemas"]["PostResponse"];

Error Handling

Huma returns RFC 9457 Problem Details:

const { data, error } = await client.POST("/auth/login", {
  body: { email, password },
});
if (error) {
  // error is typed based on response schemas
  console.error(error.detail); // human-readable message
  console.error(error.status); // HTTP status code
  console.error(error.errors); // validation details
}

Svelte Integration

<script lang="ts">
    import { client, type Workspace } from '$lib/api/client';

    let workspaces = $state<Workspace[]>([]);

    async function load() {
        const { data, error } = await client.GET('/workspaces');
        if (!error && data) {
            workspaces = data;
        }
    }
</script>

Workflow

  1. Backend defines types via Huma → auto-generates OpenAPI spec
  2. Frontend fetches spec from /openapi.json
  3. openapi-typescript generates types.d.ts
  4. openapi-fetch provides fully typed client
  5. Both sides stay in sync — type mismatches caught at compile time

Scripts

{
  "scripts": {
    "generate:types": "openapi-typescript openapi.json -o src/lib/api/types.d.ts"
  }
}

Gotchas

  1. Fields named Status in OpenAPI schemas get treated as HTTP status codes — avoid on response body structs
  2. $schema field appears in generated types (Huma JSON Schema metadata) — ignore it in client code
  3. 0001-01-01T00:00:00Z is Go's zero time — handle in frontend date formatting
  4. openapi-typescript requires OpenAPI 3.0 or 3.1 (Huma generates 3.1)

Signals

GitHub stars
138
Forks
17
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
openapi-typescript
Source
github.com/getopenpost/openpost