openapi-typescript and swagger-typescript-api are both tools designed to convert OpenAPI (formerly Swagger) specifications into TypeScript definitions, enabling type-safe API interactions in frontend applications.
openapi-typescript focuses strictly on generating static TypeScript types and interfaces that mirror the schema defined in your OpenAPI document. It does not generate network clients, focusing instead on providing the raw type definitions for request bodies, response payloads, and parameters. This approach gives developers full control over how they implement network logic while ensuring type safety.
swagger-typescript-api, on the other hand, is a more comprehensive solution that generates not only TypeScript types but also a ready-to-use API client class. It includes methods for making HTTP requests, handling parameters, and managing responses out of the box. This tool aims to reduce boilerplate by providing a complete integration layer between your frontend code and the backend API.
When building type-safe frontend applications, manually defining interfaces for every API endpoint is error-prone and hard to maintain. Both openapi-typescript and swagger-typescript-api solve this by reading your OpenAPI specification (JSON or YAML) and generating TypeScript code automatically. However, they take fundamentally different approaches to what that generated code should look like.
The most significant difference lies in the scope of generation. One tool gives you the blueprints; the other builds the house.
openapi-typescript generates only types. It converts your schema into TypeScript interfaces and types. It assumes you will write your own fetch logic or use a library like axios or react-query. This keeps the generated code stable and decoupled from any specific networking implementation.
# openapi-typescript usage
npx openapi-typescript ./api.yaml -o ./src/api/types.ts
// Resulting output from openapi-typescript
// src/api/types.ts
export interface paths {
"/user/{id}": {
get: {
parameters: { path: { id: number } };
responses: {
200: { content: { "application/json": { name: string; id: number } } };
404: { content: { "application/json": { error: string } } };
};
};
};
}
// You use these types with your own fetcher
function getUser(id: number) {
return fetch(`/user/${id}`).then(res => res.json()) as Promise<paths["/user/{id}"]["get"]["responses"][200]["content"]["application/json"]>;
}
swagger-typescript-api generates a full API client. It creates types AND a class with methods for every endpoint. It handles the HTTP request construction, parameter injection, and response parsing for you.
# swagger-typescript-api usage
npx swagger-typescript-api -p ./api.yaml -o ./src/api -n api.ts
// Resulting output from swagger-typescript-api
// src/api/api.ts
export class Api {
getUser({ id }: { id: number }) {
return this.http.request({
path: `/user/${id}`,
method: "GET",
});
}
}
// Usage is immediate and concrete
const api = new Api({ baseUrl: "https://api.example.com" });
const user = await api.getUser({ id: 123 });
How you actually make network requests differs wildly between the two.
openapi-typescript requires you to bring your own HTTP client. This is often preferred in modern React ecosystems where developers use hooks like useQuery from TanStack Query. You simply pass the generated types to these libraries.
// Using openapi-typescript types with TanStack Query
import { useQuery } from "@tanstack/react-query";
import type { paths } from "./api/types";
type UserResponse = paths["/user/{id}"]["get"]["responses"][200]["content"]["application/json"];
function useUser(id: number) {
return useQuery<UserResponse>({
queryKey: ["user", id],
queryFn: () => fetch(`/user/${id}`).then((r) => r.json()),
});
}
swagger-typescript-api bundles the HTTP logic. By default, it uses axios internally (though configurable), meaning you don't need to install or configure a separate client for basic usage. The generated code encapsulates the request logic.
// Using swagger-typescript-api generated client
import { Api } from "./api/api";
const api = new Api({
baseUrl: "https://api.example.com",
tokens: { access_token: "my-token" }
});
// The client handles the fetch/axios call internally
const { data } = await api.getUser({ id: 123 });
Customization needs often dictate which tool fits better.
openapi-typescript is highly configurable regarding type generation. You can transform types, add custom properties, or change how specific formats (like dates) are represented. It does not care about runtime behavior, so configuration is purely about the shape of the TypeScript output.
// openapi-ts.config.js
export default {
input: "./api.yaml",
output: "./src/api/types.ts",
transform: (schema) => {
// Custom logic to modify types before generation
if (schema.format === "date-time") {
return "string & { __brand: 'DateTime' }";
}
},
};
swagger-typescript-api focuses on runtime customization. You can configure the generated client to use a specific HTTP instance, add interceptors, or change how errors are handled. It allows you to inject your own axios instance or fetch implementation into the generated class.
// swagger-typescript-api config via CLI or JS API
const generateApi = require("swagger-typescript-api").generateApi;
generateApi({
name: "api.ts",
input: "./api.yaml",
output: "./src/api",
axios: true, // Explicitly use axios
modular: true, // Split into separate files
// You can pass a custom template to change how the client code looks
});
When your backend team updates the API, your frontend build process needs to adapt.
openapi-typescript excels here because types are cheap to regenerate. Since it doesn't generate logic, merging changes is usually seamless. If an endpoint changes, your TypeScript compiler will immediately flag errors in your custom fetch hooks, guiding you to the exact lines that need updating.
// If backend changes 'id' from number to string:
// openapi-typescript updates the type:
// path: { id: string }
// Your code breaks safely:
// ❌ Error: Type 'number' is not assignable to type 'string'
const user = await getUser(123);
swagger-typescript-api regenerates the entire client class. If you have extended the generated class or relied on internal private methods (which is discouraged but happens), updates can be disruptive. However, if you strictly use the public interface, the transition is smooth. The risk is higher if you try to modify the generated file directly instead of wrapping it.
// If backend changes structure, the generated method signature changes:
// api.getUser({ id: string })
// If you wrapped the client correctly, you only update the call site:
// ✅ Safe if using the public interface
const user = await api.getUser({ id: "123" });
You are building a dashboard using React, TypeScript, and TanStack Query. You want full control over caching, retries, and request deduplication.
openapi-typescript// Pattern with openapi-typescript
import type { paths } from "./types";
const { data } = useQuery({
queryKey: ["todos"],
queryFn: async () => {
const res = await fetch("/todos");
return res.json() as paths["/todos"]["get"]["responses"][200]["content"]["application/json"];
}
});
You need to connect a Vue or Angular frontend to an existing Swagger-enabled backend quickly. The team doesn't have a standardized HTTP layer yet.
swagger-typescript-apiapi.getUsers() immediately without writing fetch wrappers.// Pattern with swagger-typescript-api
import { Api } from "./api-client";
const client = new Api({ baseUrl: "..." });
// Ready to use immediately
const users = await client.getUsers();
You have a monorepo where both the Express/NestJS backend and the React frontend share the same OpenAPI spec. You want both sides to use the exact same types.
openapi-typescript// Shared types in monorepo
// packages/shared-types/src/index.ts
export * from "openapi-typescript-generated";
// Both server and client import from here
import type { paths } from "@my-org/shared-types";
| Feature | openapi-typescript | swagger-typescript-api |
|---|---|---|
| Primary Output | TypeScript Interfaces/Types | API Client Class + Types |
| HTTP Logic | None (Bring your own) | Included (Axios/Fetch based) |
| Flexibility | High (Decoupled from network) | Medium (Opinionated client structure) |
| Best For | Custom stacks, TanStack Query users | Rapid prototyping, standardized clients |
| Configuration | Type transformation focused | Runtime/Client behavior focused |
| Learning Curve | Low (Just types) | Medium (Need to understand generated class) |
The choice comes down to control vs. convenience.
If you are building a modern application where you carefully manage network state, caching, and error handling (common with React Query, SWR, or Redux), choose openapi-typescript. It gives you the safety of types without forcing a specific way of making requests. It respects your architecture.
If you need to get up and running fast, or if your team prefers a standard, generated service layer to avoid writing repetitive fetch code, choose swagger-typescript-api. It trades some flexibility for the convenience of a ready-made API client that works out of the box.
Choose openapi-typescript if you already have a preferred way of making HTTP requests (like fetch, axios, or tanstack-query) and only need the type definitions to ensure data integrity. It is ideal for teams that want to keep their network logic separate from their type definitions or need to support complex type transformations that a generated client might not handle well. This tool offers maximum flexibility and integrates seamlessly into existing custom networking stacks.
Choose swagger-typescript-api if you want a rapid setup that provides both types and a functional API client without writing boilerplate code. It is suitable for projects where speed of implementation is critical, or for teams that prefer a standardized, generated approach to API consumption. This package is also a strong choice if your OpenAPI spec includes specific vendor extensions that the tool can leverage to customize the generated client behavior.
openapi-typescript turns OpenAPI 3.0 & 3.1 schemas into TypeScript quickly using Node.js. No Java/node-gyp/running OpenAPI servers necessary.
The code is MIT-licensed and free for use.
Tip: New to OpenAPI? Speakeasy’s Intro to OpenAPI is an accessible guide to newcomers that explains the “why” and “how” of OpenAPI.
Note: OpenAPI 2.x is supported with versions 5.x and previous
This library requires the latest version of Node.js installed (20.x or higher recommended). With that present, run the following in your project:
npm i -D openapi-typescript typescript
And in your tsconfig.json, to load the types properly:
{
"compilerOptions": {
+ "module": "ESNext", // or "NodeNext"
+ "moduleResolution": "Bundler" // or "NodeNext"
}
}
Highly recommended
Also adding the following can boost type safety:
{ "compilerOptions": { + "noUncheckedIndexedAccess": true } }
First, generate a local type file by running npx openapi-typescript, first specifying your input schema (JSON or YAML), and where you’d like the --output (-o) to be saved:
# Local schema
npx openapi-typescript ./path/to/my/schema.yaml -o ./path/to/my/schema.d.ts
# 🚀 ./path/to/my/schema.yaml -> ./path/to/my/schema.d.ts [7ms]
# Remote schema
npx openapi-typescript https://myapi.dev/api/v1/openapi.yaml -o ./path/to/my/schema.d.ts
# 🚀 https://myapi.dev/api/v1/openapi.yaml -> ./path/to/my/schema.d.ts [250ms]
Then in your TypeScript project, import types where needed:
import type { paths, components } from "./my-openapi-3-schema"; // generated by openapi-typescript
// Schema Obj
type MyType = components["schemas"]["MyType"];
// Path params
type EndpointParams = paths["/my/endpoint"]["parameters"];
// Response obj
type SuccessResponse =
paths["/my/endpoint"]["get"]["responses"][200]["content"]["application/json"]["schema"];
type ErrorResponse =
paths["/my/endpoint"]["get"]["responses"][500]["content"]["application/json"]["schema"];
From here, you can use these types for any of the following (but not limited to):