Guide

OpenAPI to TypeScript SDK

Turn any OpenAPI 3.x document into a published, auto-rebuilding TypeScript SDK — without adding a single file to your API repo.

Last reviewed: April 23, 2026

Most teams who ship a backend with an OpenAPI document eventually want a TypeScript SDK to go with it. The reason is usually mundane: the frontend team is tired of hand-writing fetch wrappers, a customer asked for a typed client, or a second service showed up and started duplicating the request shapes. What they discover is that "generate a TypeScript SDK from OpenAPI" is three separate problems pretending to be one.

The first problem is generation quality. A lot of off-the-shelf generators produce code that looks generated — wide `any` on response bodies, a single AxiosError for every failure, no validation at the wire boundary, inconsistent naming. The second is the pipeline: every regeneration becomes a PR, and the SDK drifts from the spec because nobody wants to babysit the CI step. The third is publishing: the generator stops at source code, and somebody on the team has to own the npm identity, version bumps, and registry credentials.

SDK Factory treats OpenAPI → TypeScript SDK as the whole loop. You paste the URL of your OpenAPI document, pick a package name and a registry, and the first version is on npm within minutes. After that, we poll the schema, detect real changes (canonicalised hash, not whitespace), rebuild, and publish — with no PR, no Action, no human in the loop.

Why the manual path hurts

What you actually spend time on when you wire this by hand — and what eventually drifts.

The output feels generated

typescript-axios gives you `AxiosResponse<any>` in too many places. typescript-fetch emits unvalidated `as T` casts. Error handling collapses into one `catch (e: any)`. You either accept that or hand-patch generated code after every regeneration — which defeats the point.

Schema drift is the default

Nobody schedules re-generation until the SDK breaks. The gap between a spec change and a published SDK is measured in sprints, not minutes, because nothing automatic closes it.

Regeneration is always a PR

CI runs the generator, opens a PR, somebody reviews the 4 000-line diff, merges, the publish step runs. That PR is pure ceremony — nothing to review that a typechecker can't — but every schema change pays it.

Publishing is left as an exercise

The generator finishes. You still need to build, version, `npm publish`, rotate tokens, and — if you're on a private registry — wire that yourself. That's usually a week of someone's time before the SDK is actually installable.

Before and after

The hand-rolled version versus the pasted-URL version — same end state, very different footprint.

Doing it by handmanually, in CI
# Install a generator. Pick one (typescript-axios? typescript-fetch?
# typescript-node? openapi-typescript? openapi-zod-client?). Each
# produces subtly different code. Commit your choice.
npm install -D @openapitools/openapi-generator-cli

# Write a config. Decide on additional-properties flags.
cat > openapitools.json <<'JSON'
{ "generator-cli": { "version": "7.5.0" } }
JSON

# Run the generator. Get a directory of source code.
npx openapi-generator-cli generate \
  -i ./openapi.yaml -g typescript-axios \
  -o ./generated --additional-properties=npmName=@acme/api-client

# Build. Version. Publish. Own the failure modes.
cd generated && npm install && npm run build
npm version patch && npm publish --access public

# Now wire a cron / GitHub Action so this repeats when the schema
# changes. Review every regenerated diff in a PR.  Rotate the npm
# token quarterly. Debug when the generator upgrades break output.
With SDK FactorySDK Factory dashboard
Paste schema URL:  https://api.example.com/openapi.yaml
Registry:          npm (public) / GitHub Packages / custom
Package name:      @acme/api-client

┌─────────────────────┐
│   Create & publish  │
└─────────────────────┘

→ First version on your registry within minutes.
→ Every future schema change triggers a rebuild automatically.
→ No files added to your API repo. No PRs. No Action to maintain.

What SDK Factory does instead

The same pain points, handled by the pipeline — not by whoever is on call this week.

One paste, first publish in minutes

Dashboard → OpenAPI URL → package name → registry → publish. No CLI, no Action, no fern/ or .speakeasy/ directory committed to your API repo. The first tarball lands on your registry while you're still on the onboarding screen.

Auto-rebuild on every schema change

We poll the schema URL, canonicalise the document (sorted keys, stable refs, whitespace stripped) and SHA-256 hash it. Same hash = nothing ships. Different hash = a fresh build kicks off within minutes and publishes a new version.

TypeScript output designed, not emitted

ESM + CJS + a single bundled `.d.ts` per entry. Zod schemas validate both sides of the wire. Errors are a discriminated union per operation, not a generic AxiosError. Retries, timeouts, and interceptors are first-class — not additional-properties flags.

Every tarball audited

The exact artifact that was published is stored in S3, keyed by deployment ID, with the schema hash that produced it. If a downstream consumer asks "what changed between 1.4.2 and 1.4.3?", the answer is two clicks away.

What a good TypeScript SDK actually contains

A TypeScript SDK is more than types wrapped around fetch. The output has to compile under strict mode across ESM and CJS consumers, ship a single bundled `.d.ts` per entry so editors don't resolve 300 files on autocomplete, and not ship source maps that leak internal comments.

Beyond the packaging, the runtime matters. Request bodies should be Zod-validated before they hit the wire — because the server will reject them anyway, and surfacing the error in your type-safe Zod error is far more useful than reading a 400 response body. Response bodies should be Zod-validated on the way in — because the server can drift from the spec, and you'd rather have a structured `ZodError` at the SDK boundary than an `as T` cast that silently returns an object missing half its fields.

Errors should be discriminated unions per operation. If the OpenAPI document says `createUser` can return `409 EmailTaken | 422 ValidationError | 500`, the SDK's `catch` block should narrow to that union — not to a generic error class that forces a runtime `instanceof` tree.

How auto-rebuild actually works

We poll the OpenAPI URL on a short cycle. Each fetch is canonicalised — keys sorted, `$ref`s stabilised, whitespace stripped — then SHA-256 hashed. Identical hash means nothing shipped; a different hash kicks off a build.

The canonicalisation is the load-bearing part. It means a developer who re-saves the YAML through a formatter, or a toolchain that emits keys in a different order, does not trigger a spurious SDK release. Only real content changes do.

Versioning follows `openapi.info.version` — so you control when a major bump ships. We don't guess a semver diff from the schema. If your spec says 1.4.3, the npm package is 1.4.3.

Who reaches for this

  • API companies shipping a first-party TypeScript SDK to their customers, without running an internal SDK team.
  • Backend teams standardising how internal services call each other — one generated SDK per service, fresh on every deploy.
  • Monorepo setups that want a versioned npm package (not a relative import) so API consumers can pin and upgrade.
  • Teams migrating off hand-written TypeScript clients that have drifted from the spec they were supposed to mirror.

FAQ

Does this work with OpenAPI 3.0 and 3.1?

Yes. Both major versions are supported. We canonicalise the document before hashing it, so minor toolchain differences (key ordering, `$ref` styles) don't cause spurious rebuilds.

Can I keep my OpenAPI document private?

The schema URL just needs to be reachable from SDK Factory. If your spec is behind authentication, point us at an authenticated endpoint with a token we can store — token storage is AES256-encrypted and rotatable. If you need a fully air-gapped setup, that's not what we do today; OpenAPI Generator self-hosted is the honest answer for that constraint.

What's in the published npm package?

A built ESM bundle, a CJS bundle for older tooling, and a single bundled `.d.ts` per entry. No source, no source maps. The Zod schemas are emitted as runtime code (not just types) so validation actually happens at install-time and at call-time.

Can I customise the generated client?

Not today — the output is opinionated on purpose (one good shape for everyone, rather than fifty flags). If you need deep output customisation, OpenAPI Generator is a better match for that workload; we'd rather ship one great default than a matrix of lukewarm ones.

How do I roll back if a new version breaks a consumer?

On the Pro tier, a rollback is one click — we re-upload the previously published tarball from S3 and flip the registry's `latest` dist-tag. No rebuild, no version drift. On lower tiers, you can pin to the previous version on the consumer side until the next spec change ships a fix.

Try it with your actual schema.

One app on the Free tier, no card required. Paste your OpenAPI URL and see the generated TypeScript SDK in minutes.