OpenAPI to TypeScript
Generate TypeScript types and Zod schemas from OpenAPI specifications using the zod-openapi CLI.
Installation
pnpm add @alt-stack/zod-openapi
# or
npm install @alt-stack/zod-openapi
CLI Usage
npx zod-openapi <input> [options]
Options
| Option | Description |
|---|---|
-o, --output <file> | Output file path (default: generated-types.ts) |
-r, --registry <file> | Registry file that registers custom schemas |
-i, --include <file> | TypeScript file to include at top of generated output |
-h, --help | Show help message |
Basic Examples
# Generate from local file
npx zod-openapi openapi.json
# Generate from URL (e.g., from your running server)
npx zod-openapi http://localhost:3000/docs/openapi.json
# Specify output file
npx zod-openapi openapi.json -o src/api-types.ts
Generated Output
Given an OpenAPI spec, the CLI generates:
- Zod schemas for all component schemas
- TypeScript types inferred from the Zod schemas
- Request schemas for params, query, headers, and body
- Response schemas for all status codes
- Lookup objects (
RequestandResponse) for easy access
Example
For an OpenAPI spec with a User schema and /users/{id} endpoint:
/**
* This file was automatically generated from OpenAPI schema
* Do not manually edit this file
*/
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
export type User = z.infer<typeof UserSchema>;
export const GetUsersIdParams = z.object({ id: z.string() });
export const GetUsersId200Response = UserSchema;
export const Request = {
'/users/{id}': {
GET: {
params: GetUsersIdParams,
},
},
} as const;
export const Response = {
'/users/{id}': {
GET: {
'200': GetUsersId200Response,
},
},
} as const;
Custom String Formats
For custom type mappings (e.g., using Luxon DateTime for iso-date format), use the --registry and --include flags.
Step 1: Create a Registry File
The registry file registers format-to-schema mappings that the CLI uses during code generation:
import { z } from "zod";
import { registerZodSchemaToOpenApiSchema } from "@alt-stack/zod-openapi";
// Register DateTimeSchema for iso-date and iso-date-time formats
const dateTimeSchema = z.string();
registerZodSchemaToOpenApiSchema(dateTimeSchema, {
schemaExportedVariableName: "DateTimeSchema",
type: "string",
formats: ["iso-date", "iso-date-time"],
});
Step 2: Create an Include File
The include file contains imports and schema definitions that will be injected at the top of the generated output:
import { DateTime } from "luxon";
export const DateTimeSchema = z
.string()
.transform((v) => DateTime.fromISO(v));
Step 3: Run the CLI
npx zod-openapi openapi.json \
-r ./registry.ts \
-i ./custom-schemas.ts \
-o src/api-types.ts
The generated output will:
- Include the contents of
custom-schemas.tsat the top - Use
DateTimeSchemafor any fields withformat: "iso-date"orformat: "iso-date-time"
Supported String Formats
The following string formats can be registered:
color-hexdatedate-timeemailiso-dateiso-date-timeobjectiduriurluuid
Integration with @alt-stack/server
A typical workflow with @alt-stack/server-hono:
- Define your API with Zod schemas on the server
- Generate OpenAPI spec using
createDocsRouter - Generate client types using the
zod-openapiCLI
# Fetch OpenAPI from your running server and generate types
npx zod-openapi http://localhost:3000/docs/openapi.json -o src/api-types.ts
This gives you fully typed request/response schemas that match your server's API exactly.
Programmatic Usage
You can also use the library programmatically:
import { openApiToZodTsCode } from "@alt-stack/zod-openapi";
const openApiSpec = {
components: {
schemas: {
User: {
type: "object",
properties: {
id: { type: "string" },
name: { type: "string" },
},
required: ["id", "name"],
},
},
},
paths: {
"/users/{id}": {
get: {
parameters: [
{ name: "id", in: "path", required: true, schema: { type: "string" } },
],
responses: {
"200": {
content: {
"application/json": {
schema: { $ref: "#/components/schemas/User" },
},
},
},
},
},
},
},
};
const generatedCode = openApiToZodTsCode(
openApiSpec,
['import { DateTime } from "luxon";'], // custom imports
{ includeRoutes: true }
);
console.log(generatedCode);