The idea
A command-line tool that fetches a live HTTP endpoint and writes a TypeScript interface from the actual response body in one step. You run api-types get https://api.example.com/users/1 --name User and get a ready-to-paste .ts file in seconds. An optional --zod flag generates a Zod validation schema alongside the interface. A --sample N flag fetches the endpoint N times and merges the results, marking any key absent in at least one response as optional.
The tool handles nested objects, arrays, string/number/boolean/null primitives, and nullable fields. No web UI, no JSON pasting, no context switching out of the terminal.
Why build this
TypeScript developers who consume third-party REST APIs spend a non-trivial chunk of their first-day integration time writing types that the response shapes already imply. Quicktype solves this problem but requires copying JSON into a browser UI or calling a complex CLI with a non-obvious invocation. Nothing does the fetch-to-type pipeline in a single terminal command aimed at developers who want to stay local.
The Zod angle is particularly underserved. Quicktype generates types but not validation schemas, and doing both from a live endpoint in one shot is the missing piece for developers building with tRPC, Hono, or any schema-first framework. The runtime validation and the static type should come from the same source — the API itself — and right now there is no tool that automates that chain end to end.
Stack sketch
- CLI: Node.js with Commander.js; distributed as a standalone binary via
pkgor as a single bundled file withesbuildso there is nonpm installstep for the end user - HTTP client:
undici— ships with Node 18+, supports custom headers and bearer tokens via CLI flags (--header,--bearer), handles redirects automatically - Type inference: a depth-first traversal over the parsed JSON value that builds a typed AST node for each key; arrays are collapsed to their inferred element type;
nullvalues produceT | nullunions - TypeScript printer: Prettier's TypeScript printer applied to the generated AST; the output is always formatted identically regardless of key ordering in the source response
- Zod output: a second pass over the same AST emits a
z.object({...})schema; the--zodflag outputs both the interface and the schema to the same file with the schema inferred from the interface type - Optional field merging:
--sample Nruns N parallel fetches viaPromise.all, collects response bodies, and marks a key optional if it is absent or null in any sample
Scope for v1
get <url>command with--name,--output,--header,--bearer,--sample, and--zodflags- Handles all JSON primitives: string, number, boolean, null
- Nested objects generate nested interfaces; arrays generate typed element interfaces or inline union types
- Nullable fields (null in any sampled response) produce
T | nullon the interface andz.nullable()on the schema - Writes to stdout by default;
--output path/to/types.tswrites to disk - Deliberately out of v1: OpenAPI spec import, GraphQL endpoints, form-encoded or multipart responses, streaming APIs, automatic file injection into an existing TypeScript project
Where it could go
A watch mode that monitors a manifest file of endpoint definitions and regenerates types whenever a response shape changes would make the tool genuinely useful in active development. Define your API surface once in YAML — URL, name, sample count, auth — and run api-types watch manifest.yml. Types regenerate on a schedule or when triggered by a git hook, so the type files in your repository stay in sync with the actual API without manual intervention.
The more ambitious expansion is API drift detection: run the same inference against the same endpoints on two environments and report every field that changed type, appeared, or disappeared. That gives you a lightweight contract testing layer that requires nothing from the API provider — no OpenAPI spec, no test environment, just the live endpoints. A CI step that runs api-types diff staging.yml production.yml would catch response shape regressions before they reach a consumer.
Watch out for
Bearer tokens and API keys passed via --bearer and --header live in the process environment during execution. Make sure the tool never writes them to disk implicitly — neither in a cache file nor in any generated output. The .ts file should contain only inferred types, never request metadata.