How to generate a TypeScript SDK from your OpenAPI spec

CJ Avilla

Developer Relations Engineer

Jump to section

Jump to section

Jump to section

If you maintain a public API, you've probably faced the same question: how do you help developers (and agents) integrate quickly without forcing them to hand-craft HTTP requests, parse JSON responses, and handle edge cases on their own?

The answer is a platform, not a single artifact. SDKs, documentation, and other tools work together to shape how developers experience your API.

This guide focuses on one component: the TypeScript SDK. For JavaScript and TypeScript developers, it is often the deciding factor between choosing your API or moving on to a competitor with better tooling.

This guide covers how to generate a TypeScript SDK from an OpenAPI specification. It covers the traditional approaches, where they fall short, and how to produce a production-ready SDK that feels hand-crafted.

Why a TypeScript SDK matters for your API

Without an SDK, developers must:

  • Read your API documentation and manually construct HTTP requests

  • Handle authentication headers, query parameter serialization, and request body formatting

  • Parse responses and map them to their own types

  • Implement error handling, retries, and pagination logic from scratch

  • Keep their integration code updated as your API evolves

A well-designed TypeScript SDK eliminates all of this. Developers get type-safe methods with autocomplete, compile-time error checking, and built-in handling for common patterns.

Integration time drops significantly.

Using raw fetch vs using an SDK to make an API call.

The payoff is faster adoption and fewer support tickets, especially when something goes wrong. Strong TypeScript types make it easier for developers and agents to detect, understand, and recover from incorrect usage.

The traditional approach: OpenAPI Generator

The legacy starting point is the open-source OpenAPI Generator. It supports dozens of languages, including TypeScript, and can scaffold a client library from your spec in minutes.

Here's the typical workflow:

# Generate a TypeScript SDK using the fetch template
openapi-generator-cli generate \
  -i path/to/openapi.yaml \
  -g typescript-fetch \
  -o

This produces a functional SDK with TypeScript interfaces for your models and a client class with methods for each endpoint.

For internal tools or quick prototypes, this works. For a public-facing SDK that represents your product, the limitations become apparent quickly.

Where basic code generation falls short

Generic, non-idiomatic code. The templates produce functional output, but method names, class structures, and patterns don't always match what TypeScript developers expect. The code feels generated, not designed.

Missing advanced features. Real APIs need pagination, retries with exponential backoff, and structured error handling. OpenAPI Generator doesn't include these. Your users must implement them manually, which leads to inconsistent behavior across integrations.

Manual maintenance burden. Every time your API changes, you re-run the generator, manually bump versions, and publish to npm. There's no built-in handling for breaking changes or automated release workflows. The onus is entirely on your team.

Limited customization. Tweaking the output requires forking Mustache templates, which quickly becomes a maintenance project of its own.

We designed Stainless for teams that run into these limits. It takes the same OpenAPI input, but optimizes for production SDKs rather than scaffolding. The table below compares the two approaches.

Feature

OpenAPI Generator

Stainless

Error Handling

Basic (ResponseError, FetchError, RequiredError)

Rich hierarchy with many specific error types

Retry Logic

None built-in

Exponential backoff with jitter, Retry-After support

Pagination

Returns raw paginated response

Auto-pagination with async iterators

Streaming (SSE)

Not supported

Full SSE parser with Stream class

API Ergonomics

Verbose method names, single object params

Clean method names, positional + optional params

Type Safety

Good (FromJSON / ToJSON transforms)

Excellent (strict mode, exactOptionalPropertyTypes)

Middleware Support

Pre/post/onError middleware

Request options per-call

File Uploads

Basic FormData/Blob

Uploadable type (File, Response, ReadStream)

Documentation

JSDoc from OpenAPI spec

Rich TSDoc with code examples

Raw Response Access

Via ApiResponse wrapper

.asResponse() and .withResponse()

Environment Config

basePath in Configuration

Multiple named environments and env vars

Module System

Single export pattern

Dual ESM/CJS with conditional exports

How to generate a TypeScript SDK from OpenAPI with Stainless: the recommended approach

Stainless generates TypeScript SDKs from OpenAPI that follow idiomatic language conventions and include production features like retries, pagination, and structured errors by default.

This approach comes from experience building large-scale SDK platforms, including work on Stripe’s SDK infrastructure, where maintaining high-quality, hand-crafted SDKs across many languages required far more than mapping endpoints to methods.

Getting started

You can create a new project from an OpenAPI spec in about one minute:

  1. Sign up for Stainless. Create an account at app.stainless.com using your GitHub account, then create an organization for yourself or your company.

  2. Create a project. Create a new Stainless project for your API by providing an OpenAPI spec. You can paste a URL to a hosted spec, upload a file, or start from an example such as the Petstore API.

Note: A Stainless project represents a single API. From one project, you can generate and manage multiple SDKs in different languages, along with related artifacts such as documentation sites, CLIs, MCP servers, or Terraform providers, all driven from the same OpenAPI spec.

Once you create the project project, Stainless generates a TypeScript SDK and opens it in the Studio inside the dashboard. Stainless also invites you to a GitHub repository containing the generated SDK code.


What you get out of the box

The generated TypeScript SDK includes retry logic, streaming, pagination, and error handling that you would otherwise build and maintain yourself. Here's what that looks like in practice, using the OpenAI Node SDK as an example.

Zero dependencies. Stainless uses the built-in fetch API for HTTP requests. The SDK works in Node.js, Deno, Bun, Cloudflare Workers, Vercel Edge Runtime, and modern browsers without pulling in a single dependency.

Full type safety. Every model, request, and response is fully typed. Developers get autocomplete for all parameters and compile-time validation of their API calls:

import OpenAI from 'openai';

const client = new OpenAI(); // Uses OPENAI_API_KEY env var by default

const response = await client.responses.create({
	model: 'gpt-5.2',
	instructions: 'You are a coding assistant that talks like a pirate',
	input: 'Are semicolons optional in JavaScript?',
});

console.log(response.output_text);

Streaming (SSE). For APIs that support Server-Sent Events, the SDK provides a Stream class with async iteration built in. No manual SSE parsing required:

const stream = await client.responses.create({
	model: 'gpt-5.2',
	input: 'Say "Sheep sleep deep" ten times fast!',
	stream: true,
});

for await (const event of stream) {
	console.log(event);
}

Auto-pagination. List endpoints return async iterators that handle page tokens automatically. Developers don't need to write pagination logic:

// Automatically fetches more pages as needed.
for await (const job of client.fineTuning.jobs.list({ limit: 20 })) {
	console.log(job);
}

// Or work with a single page at a time:
let page = await client.fineTuning.jobs.list({ limit: 20 });
for (const job of page.data) {
	console.log(job);
}

Automatic retries. The SDK automatically retries for connection errors, 408 timeouts, 429 rate limits, and 5xx server errors with exponential backoff. Developers can configure this globally or per-request:

// Configure the default for all requests:
const client = new OpenAI({
	maxRetries: 0, // default is 2
});

// Or override per-request:
await client.chat.completions.create(
	{ model: 'gpt-5.2', messages: [{ role: 'user', content: 'Hello!' }] },
	{ maxRetries: 5 },
);

Configuring, publishing, and keeping SDKs in sync

Beyond what's generated out of the box, Stainless handles the operational side of SDK management:

SDK configuration. A stainless.yaml file controls how your API maps to SDK methods: resource grouping, pagination schemes, method naming, and more. The dashboard includes an LSP-powered editor with autocomplete and inline documentation.

Automated publishing. Connect your npm account and new SDK versions publish automatically when your API spec changes. Stainless supports secure OIDC-based trusted publishing through GitHub Actions.

Automated regeneration. A GitHub Action in your API repository keeps SDKs in sync automatically: open a PR that changes your spec and Stainless previews the SDK diff; merge it and release PRs are opened across all your SDK repos.

Real-world examples

Stainless generates the official, production SDKs for several major API providers, collectively handling millions of API calls daily:

Conclusion

Basic code generation tools work for internal tools or prototypes. For a public SDK that represents your product, you need production-ready features: zero dependencies, full type safety, built-in pagination and retries, and automated publishing.

Stainless generates TypeScript SDKs with these features built in. The result is faster adoption, fewer support tickets, and a stronger API platform.

Ready to generate your TypeScript SDK? Get started with Stainless or read the documentation to learn more.

Originally posted

Feb 9, 2026