Skip to content

01Start

Introduction

What swaggerz is, what it generates, and when another tool is the better fit.

swaggerz is a compiler. Its input is an API description: Swagger 2.0, or OpenAPI 3.0, 3.1 or 3.2, optionally patched by OpenAPI Overlays. Its output is the TypeScript or JavaScript client you would have written by hand: one function per operation, one type per model, typed errors, and nothing you do not import.

The generated code lives in your repository and has no dependency on swaggerz. You can read it, debug it and step through it; swaggerz is only a development dependency.

  • A function per operation, taking one params object, resolving with typed data and rejecting with errors typed per HTTP status.
  • A type per model, with documentation from the specification, separate request and response types where readOnly and writeOnly differ, and Date values if you want them.
  • A small runtime in the client itself: URL building, parameter and body serialization (every style OpenAPI defines), authentication, middleware, retries and token refresh, over fetch or axios.
  • Optionally, TanStack Query hooks for React, Vue, Svelte and Solid, or SWR hooks, and Zod, Valibot or ArkType schemas that validate responses at runtime.
  • Tools around the client: a linter for what makes the client worse, a local dashboard, and an MCP server that gives coding agents the exact generated functions.

It is a compiler, not a template. Every specification version has its own adapter into one intermediate representation, so a Swagger 2.0 document and an OpenAPI 3.2 one produce the same kind of code, and features the client cannot represent are reported as diagnostics with a location instead of being silently dropped. The pipeline page shows the stages.

Every operation is its own module. There is no class per tag and no registry of endpoints, so bundlers keep only the operations an application imports, measured with esbuild, Rollup, Vite and webpack on tree-shaking.

Regeneration is safe. The same input gives byte-identical output, unchanged files are not rewritten, and files the generator does not own are never overwritten or deleted. See determinism and safety.

  • You want types only, and call the API by path with a thin fetch wrapper: openapi-typescript generates one declaration file and stays out of your runtime.
  • You need server code: swaggerz generates clients. For webhooks and callbacks it generates handler types, but no routing or server stubs.
  • You need a language other than TypeScript or JavaScript: openapi-generator covers dozens.
  • Your API is not described by Swagger or OpenAPI: GraphQL, gRPC and AsyncAPI have their own generators.

A specification on the left, the code swaggerz generates for it on the right. Hover a line on either side to see its counterpart.

petstore.yaml
openapi: 3.1.0
info:
title: Petstore
version: '1.0'
servers:
- url: https://petstore.example.com/v1
paths:
/pets/{petId}:
get:
tags: [pets]
summary: Find a pet by ID.
description: Returns a single pet.
operationId: getPetById
parameters:
- name: petId
in: path
required: true
description: ID of the pet to return.
schema:
type: integer
format: int64
minimum: 1
responses:
'200':
description: The pet.
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
'404':
description: No pet has this ID.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- api_key: []
components:
securitySchemes:
api_key:
type: apiKey
in: header
name: X-API-Key
schemas:
Pet:
type: object
required: [id, name]
properties:
id:
type: integer
format: int64
name:
type: string
example: Rex
status:
type: string
description: Where the pet is in the store.
enum: [available, pending, sold]
Error:
type: object
required: [message]
properties:
message:
type: string

pets/get-pet-by-id.ts

/* eslint-disable */
// @generated by swaggerz. Do not edit: this file is overwritten on the next generation.
import { type RequestOptions, client } from '../client.js';
import type { Error as ErrorModel } from '../models/error.js';
import type { Pet } from '../models/pet.js';
import { type ApiError, type RequestError, isApiError } from '../runtime/errors.js';
import type { HttpStatus } from '../runtime/types.js';
/** Parameters of {@link getPetById}. */
export type GetPetByIdParams = {
/**
* ID of the pet to return.
*
* @format int64
* @minimum 1
*/
petId: number;
};
/** Response data of {@link getPetById}. */
export type GetPetByIdResponse = Pet;
/** Body of an error response of {@link getPetById}. */
export type GetPetByIdErrorBody = ErrorModel;
/**
* The `ApiError`s {@link getPetById} rejects with: one per documented status, so checking `status` narrows `body`.
*
* The last member covers statuses the specification does not document.
*/
export type GetPetByIdApiError =
| ApiError<ErrorModel, 404>
| ApiError<unknown, Exclude<HttpStatus<1 | 3 | 4 | 5>, 404>>;
/** Everything {@link getPetById} rejects with: an `ApiError` for an error status, or a `RequestError` when no usable response arrived. */
export type GetPetByIdError = GetPetByIdApiError | RequestError;
/**
* Find a pet by ID.
*
* Returns a single pet.
*
* @remarks `GET /pets/{petId}` (operationId `getPetById`)
* @param params - Request parameters.
* @param options - Per-request options: `signal`, `timeout`, `headers`, `client`.
* @returns `200` The pet
* @throws {@link ApiError} `404` No pet has this ID; narrow it with {@link isGetPetByIdApiError}.
* @throws {@link RequestError} When no usable response is received (network failure, timeout or invalid body).
*/
export function getPetById(params: GetPetByIdParams, options?: RequestOptions): Promise<GetPetByIdResponse> {
return (options?.client ?? client).request<GetPetByIdResponse>(
{
operationId: 'getPetById',
method: 'GET',
path: '/pets/{petId}',
pathParams: { petId: params.petId },
accept: 'application/json',
security: [['api_key']],
},
options,
);
}
/** Whether `error` is an `ApiError` rejected by {@link getPetById}. It narrows to {@link GetPetByIdApiError}, so checking `status` afterwards narrows `body` too. */
export function isGetPetByIdApiError(error: unknown): error is GetPetByIdApiError {
return (
isApiError(error) &&
error.operationId === 'getPetById' &&
error.method === 'GET'
);
}

models/pet.ts

/* eslint-disable */
// @generated by swaggerz. Do not edit: this file is overwritten on the next generation.
export type Pet = {
/** @format int64 */
id: number;
/** @example "Rex" */
name: string;
/** Where the pet is in the store. */
status?: 'available' | 'pending' | 'sold';
};
Generated by swaggerz 0.1.2, at build time.

Next: generate a client for your API in the quick start.