Codegen
use-q-codegen turns an OpenAPI 3.x document (JSON or YAML) into a fully-typed RouteDefinition map you can drop straight into createApiClient or createFetcher.
Running the CLI
Section titled “Running the CLI”The binary is shipped inside @use-q/api-client, so once you’ve installed the core package you can call it via your package manager:
pnpm exec use-q-codegen --input ./openapi.json --output ./src/api/schema.tsnpx use-q-codegen --input ./openapi.yaml --output ./src/api/schema.ts --base-url https://api.example.comCLI flags
Section titled “CLI flags”| Flag | Description |
|---|---|
--input | Path to an OpenAPI 3.x spec (.json or .yaml). Required. |
--output | Path to write the generated schema.ts. Required. |
--base-url | Optional. If provided, emitted as a BASE_URL constant alongside the schema. |
YAML inputs use js-yaml under the hood, so anchors and multi-line strings are supported.
What gets generated
Section titled “What gets generated”The emitter produces a single TypeScript file with three sections:
// 1. Component schemas → TS interfacesexport interface Post { id: string; facilityId: string; title: string; body: string; createdAt: string;}
// 2. Operation-level types (request bodies, parameters)export interface CreatePostInput { title: string; body: string;}
// 3. RouteDefinition map, ready for createApiClientimport type { Schema } from "@use-q/api-client";
export const schema = { listPosts: { method: "GET", path: "/facilities/{facilityId}/posts", pathParams: {} as { facilityId: string }, searchParams: {} as { search?: string; page?: number; limit?: number }, response: {} as { items: Post[]; total: number }, pagination: { kind: "page-number", pageParam: "page", itemsField: "items", totalField: "total", }, }, createPost: { method: "POST", path: "/facilities/{facilityId}/posts", pathParams: {} as { facilityId: string }, body: {} as CreatePostInput, response: {} as Post, }, // …} as const satisfies Schema;Route names use the OpenAPI operationId. If an operation has no operationId, the emitter falls back to methodPathSegments (e.g. GET /posts/{id} → getPostsId).
$ref resolution
Section titled “$ref resolution”$ref pointers within components/schemas are inlined as TypeScript interfaces. Composition keywords are handled like so:
| OpenAPI | TS |
|---|---|
allOf | intersection (A & B) |
oneOf | discriminated union (A | B) |
anyOf | union (A | B) |
enum | string-literal union |
nullable: true | T | null |
Pagination detection
Section titled “Pagination detection”The emitter inspects the response schema and infers a pagination block automatically:
| Response shape | Inferred pagination.kind |
|---|---|
Object with both items and total (or count) | "page-number" |
Object with nextCursor (or cursor) | "cursor" |
| Array or anything else | omitted |
The pageParam/cursorParam is taken from the parameter named page, pageNumber, or cursor in the operation. If none is found and pagination shape is detected, the emitter warns to stderr and skips emitting the pagination block — you can hand-add it.
When to hand-edit
Section titled “When to hand-edit”Codegen handles ~90% of the boilerplate, but a few things should be hand-tuned:
-
Tags —
tagsandinvalidatesTagsaren’t part of OpenAPI. Add them after codegen so you keep them on regenerations:// Hand-added after codegenschema.listPosts.tags = [{ type: "post", facilityId: (p) => p.facilityId },];…or keep the generated file pristine and compose tags in a sibling file:
src/api/schema.tags.ts import { schema as base } from "./schema.generated";export const schema = {...base,listPosts: {...base.listPosts,tags: [{ type: "post", facilityId: (p) => p.facilityId }],},} as const; -
Custom error types. Codegen emits a default
unknownparsed-error type. Pair it with your ownparseErrorincreateApiClientto surfaceApiProblem/RFC 7807 fields. -
Renaming routes. If
operationIdis missing or ugly, rename keys in the generated file. The TS-onlypathParams/responsewill follow.
Running as part of your build
Section titled “Running as part of your build”A common setup:
{ "scripts": { "codegen": "use-q-codegen --input ./openapi.json --output ./src/api/schema.generated.ts", "prebuild": "pnpm codegen" }}For monorepos:
- Put the OpenAPI spec in a
packages/api-specpackage. - Generate the schema into a shared
packages/api-schemapackage. - Import
schemafrom@org/api-schemainto both web and CLI consumers.
See Monorepo usage for a full walkthrough.
Programmatic API
Section titled “Programmatic API”For build scripts or codemods, you can call the generator directly:
import { generate } from "@use-q/api-client-codegen";
await generate({ input: "./openapi.json", output: "./src/api/schema.ts", baseUrl: "https://api.example.com",});generate resolves once the file has been written and Prettier-formatted (using your project’s config if found).
Formatting
Section titled “Formatting”The emitter calls Prettier with your project’s resolved config (or its defaults). To turn that off, set format: false in the programmatic API. The CLI always formats.