npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zodapi/codegen

v0.5.0

Published

Generate a zodapi contract (zod schemas + route defs) from an OpenAPI 3.1 document

Readme

@zodapi/codegen

Generates a zodapi contract — zod schemas plus plain RouteDef route objects — from an OpenAPI 3.1 document. For backends not written in TypeScript: point it at the spec your Python/Go/.NET/... framework emits and consume the result with @zodapi/client.

zodapi-codegen openapi.json -o contract.ts

or programmatically:

import { generateContract } from '@zodapi/codegen'

const source = generateContract(JSON.parse(await readFile('openapi.json', 'utf8')))

What it generates

One file, importing only zod and @zodapi/core:

  • an exported const per components/schemas entry — component name = const name; recursive schemas use zod 4 shape getters
  • an exported const per operation, a plain object satisfies RouteDef — operationId becomes the client alias (no alias without one); the spec's declared responses are taken verbatim, nothing (like the zodapi 400) is injected
  • a routes tuple ready for createClient(routes) from @zodapi/client
  • a problemFlavor const ('zodapi' | 'problem-details' | undefined, detected from the spec's error responses — zodapi's urn:zodapi:validation problem type, or an ASP.NET-style ValidationProblemDetails) to feed decodersFor(problemFlavor) when creating the client
  • with exportTypes: true (CLI --export-types), a type <Name> = z.infer<typeof <Name>> alias alongside each component const (the zodios codegen convention). z.infer is the output side — use z.input<typeof <Name>> where the wire form differs (e.g. date codecs)

Documentation output

The spec's documentation (title, description, examples, deprecated, operation summaries/tags) is emitted per docs: 'jsdoc' | 'meta' | 'none' (CLI --docs <mode>):

  • 'jsdoc' (default): JSDoc comments on component consts, object properties, parameters, and route consts (summary/description body plus a @tags line) — hover docs in the editor, zero runtime weight. No .meta() calls, no route doc fields.
  • 'meta': full fidelity — runtime .meta({...}) calls, .meta({ id }) component registration (so the schemas re-serialize as the same $ref components), and operationId/summary/ description/tags fields on route objects. Use this when you regenerate a spec from the contract.
  • 'none': documentation is dropped entirely. default values, response descriptions, and alias are structural and kept in every mode.

Query parameters typed array are declared with queryArray(item) from @zodapi/core, matching the zodapi a[]= convention.

Output is unformatted; run your formatter over it.

Date conversion

By default ISO strings stay strings (z.iso.datetime() / z.iso.date()). Opt in to Date conversion per format:

zodapi-codegen openapi.json -o contract.ts --dates-datetime --dates-date --dates-offset
generateContract(doc, { dates: { datetime: true, date: true, offset: true } })
  • datetime / --dates-datetime: format: date-time fields become a bidirectional z.codec(z.iso.datetime(), z.date(), ...) — responses parse to Date, requests encode back to the wire string
  • date / --dates-date: format: date fields become a codec decoding to Date at UTC midnight and encoding back to YYYY-MM-DD
  • offset / --dates-offset: accept UTC offsets in date-time values (z.iso.datetime({ offset: true }))

The codecs are emitted once as shared isoDatetimeToDate / isoDateToDate consts; a field with extra wire-side constraints or a default inlines the codec with those applied to its input side.

Because codecs change parsed values, @zodapi/client refuses calls whose validation mode would skip a codec-bearing schema (see the client README); pair a dates contract with validate: 'response' (the default) or 'both'. Codec-bearing params and query values always take Date objects; add encodeRequests: true to pass them in request bodies too.

Fidelity

The converter covers the JSON Schema subset OpenAPI 3.1 uses: objects (required/optional, additionalProperties as loose objects, catchalls, and records), arrays and tuples, unions (oneOf is treated as anyOf), intersections (allOf), nullability in both encodings, enums and consts, string formats (email, uuid, uri, date-time, ...), numeric/string/array constraints, defaults, and description/title/examples metadata.

It is enforced by a round-trip test: a comprehensive hand-written contract is serialized to OpenAPI, fed through the generator with docs: 'meta', and the document emitted from the generated contract must deep-equal the original. ('jsdoc'/'none' contracts are deliberately lossy — no component ids, no runtime metadata — so only 'meta' round-trips.)

Not covered

webhooks, refs outside #/components/schemas (component parameters/responses), response headers, and OpenAPI 3.0 documents (3.1 only — 3.1 schemas are real JSON Schema, 3.0's nullable dialect is not).

Install

pnpm add -D @zodapi/codegen

The generated file needs zod and @zodapi/core at runtime.