VeztaVezta
Guides

API Client Generation

OpenAPI to TypeScript with Kubb code generation

Vezta uses an automated pipeline to keep the frontend and mobile API clients in sync with the backend. The backend exports an OpenAPI spec, and Kubb generates TypeScript types, Zod schemas, fetch clients, and TanStack Query hooks from it.

The Pipeline

The mobile app (when implemented) will use the same pipeline -- the generated lib/api/gen/ directory gets manually copied from vezta-fe.

Step 1: Export the OpenAPI Spec

The backend auto-exports openapi.json to its project root on every startup. You can also export it manually:

cd vezta-be && pnpm export:openapi

The spec is also available at http://localhost:3001/docs-json when the dev server is running. Swagger UI is at http://localhost:3001/docs.

Step 2: Generate the Frontend Client

With the backend running (or with a local openapi.json file):

cd vezta-fe && pnpm generate

This runs Kubb with the configuration in kubb.config.ts, reading the local openapi.json file (path via the OPENAPI_PATH env var, default ./openapi.json). pnpm generate does not fetch from the backend. To pull a fresh spec first, run pnpm generate:api, which curls ${OPENAPI_URL:-http://localhost:3001/docs-json} into openapi.json and then generates.

Kubb Configuration

The kubb.config.ts file configures five plugins in sequence:

plugins: [
  // 1. Parse and validate the OpenAPI spec
  pluginOas({ validate: true }),

  // 2. TypeScript interfaces (enums as const, dates as string)
  pluginTs({ output: { path: "models" }, enumType: "asConst", dateType: "string" }),

  // 3. Zod schemas for runtime validation
  pluginZod({ output: { path: "zod" }, dateType: "string", inferred: true }),

  // 4. Typed fetch client functions
  pluginClient({ output: { path: "client" }, client: "fetch", dataReturnType: "data" }),

  // 5. TanStack Query v5 hooks (grouped by API tag, suspense enabled)
  pluginReactQuery({
    output: { path: "hooks" },
    client: { importPath: "@kubb/plugin-client/clients/fetch" },
    group: { type: "tag" },
    suspense: {},
  }),
]

The output goes to lib/api/gen/ with clean: true, meaning the entire directory is wiped and regenerated on each run.

Fetch Alias

Both webpack and turbopack alias @kubb/plugin-client/clients/fetch to lib/api/kubb-fetch.ts in next.config.ts. This means every generated hook automatically gets:

  • Bearer token injection from the in-memory access token
  • Automatic 401 refresh-and-retry logic
  • credentials: 'include' for cookie-based auth

No hand-editing of generated code is needed.

Step 3: Sync to Docs

The docs site renders the API reference directly from public/openapi.json via fumadocs-openapi and a tag-grouped MDX generator:

# Copy the spec from the backend to the docs site
cp vezta-be/openapi.json vezta-docs/public/openapi.json

# Regenerate API reference MDX (grouped by Swagger tag)
cd vezta-docs && pnpm generate:api

The MDX files in vezta-docs/content/api/ may be hand-edited for prose, but generated sections will be overwritten by pnpm generate:api. The interactive playground on each endpoint reads public/openapi.json directly.

Never hand-edit files in lib/api/gen/ in any subproject. The directory is wiped and regenerated with clean: true on every pnpm generate run. Any manual changes will be lost.

When to Regenerate

Run pnpm generate in vezta-fe after any backend API change:

  • New endpoint added to a controller
  • DTO field added, removed, or renamed
  • Response shape changed
  • Swagger decorators updated (@ApiProperty, @ApiOperation, @ApiResponse)
  • New controller or module registered

Kubb only generates offset-based pagination hooks. Cursor-based infinite queries (used for feeds, activity logs, etc.) must be wrapped manually in feature hooks using TanStack Query's useInfiniteQuery.

Troubleshooting

ProblemSolution
pnpm generate fails to connectEnsure the backend is running on port 3001, or set OPENAPI_PATH to a local openapi.json file
Generated types are staleRe-export the spec (pnpm export:openapi in vezta-be) and regenerate
Missing hooks for new endpointVerify the controller has @ApiTags and @ApiOperation decorators
Docs site reference is staleCopy vezta-be/openapi.jsonvezta-docs/public/openapi.json and run pnpm generate:api
Backend pnpm export:openapi fails with @/ alias errorRun pnpm build first, then export from dist/. The spec is also auto-exported on dev server startup.

On this page