openapi-fetch is a type-safe fetch client that pulls in your OpenAPI schema. Weighs 6 kb and has virtually zero runtime. Works with React, Vue, Svelte, or vanilla JS.
Library | Size (min) | “GET” request* |
---|---|---|
openapi-fetch | 6 kB | 300k ops/s (fastest) |
openapi-typescript-fetch | 3 kB | 300k ops/s (fastest) |
feature-fetch | 15 kB | 300k ops/s (fastest) |
axios | 32 kB | 225k ops/s (1.3× slower) |
superagent | 55 kB | 50k ops/s (6× slower) |
openapi-typescript-codegen | 367 kB | 100k ops/s (3× slower) |
* Benchmarks are approximate to just show rough baseline and will differ among machines and browsers. The relative performance between libraries is more reliable.
The syntax is inspired by popular libraries like react-query or Apollo client, but without all the bells and whistles and in a 6 kb package.
import createClient from "openapi-fetch";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
const {
data, // only present if 2XX response
error, // only present if 4XX or 5XX response
} = await client.GET("/blogposts/{post_id}", {
params: {
path: { post_id: "123" },
},
});
await client.PUT("/blogposts", {
body: {
title: "My New Post",
},
});
data
and error
are typechecked and expose their shapes to Intellisense in VS Code (and any other IDE with TypeScript support). Likewise, the request body
will also typecheck its fields, erring if any required params are missing, or if there’s a type mismatch.
GET()
, PUT()
, POST()
, etc. are thin wrappers around the native fetch API (which you can swap for any call).
Notice there are no generics, and no manual typing. Your endpoint’s request and response were inferred automatically. This is a huge improvement in the type safety of your endpoints because every manual assertion could lead to a bug! This eliminates all of the following:
- ✅ No typos in URLs or params
- ✅ All parameters, request bodies, and responses are type-checked and 100% match your schema
- ✅ No manual typing of your API
- ✅ Eliminates
any
types that hide bugs - ✅ Also eliminates
as
type overrides that can also hide bugs - ✅ All of this in a 6 kb client package 🎉
Setup
Install this library along with openapi-typescript:
npm i openapi-fetch
npm i -D openapi-typescript typescript
Highly recommended
Enable noUncheckedIndexedAccess in your tsconfig.json
(docs)
Next, generate TypeScript types from your OpenAPI schema using openapi-typescript:
npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts
Lastly, be sure to run typechecking in your project. This can be done by adding tsc --noEmit
to your npm scripts like so:
{
"scripts": {
"test:ts": "tsc --noEmit"
}
}
And run npm run test:ts
in your CI to catch type errors.
TIP
Use tsc --noEmit
to check for type errors rather than relying on your linter or your build command. Nothing will typecheck as accurately as the TypeScript compiler itself.
Basic usage
The best part about using openapi-fetch over oldschool codegen is no documentation needed. openapi-fetch encourages using your existing OpenAPI documentation rather than trying to find what function to import, or what parameters that function wants:
import createClient from "openapi-fetch";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
const { data, error } = await client.GET("/blogposts/{post_id}", {
params: {
path: { post_id: "my-post" },
query: { version: 2 },
},
});
const { data, error } = await client.PUT("/blogposts", {
body: {
title: "New Post",
body: "<p>New post body</p>",
publish_date: new Date("2023-03-01T12:00:00Z").getTime(),
},
});
- The HTTP method is pulled directly from
createClient()
- You pass in your desired
path
toGET()
,PUT()
, etc. - TypeScript takes over the rest and returns helpful errors for anything missing or invalid
Pathname
The pathname of GET()
, PUT()
, POST()
, etc. must match your schema literally. Note in the example, the URL is /blogposts/{post_id}
. This library will quickly replace all path
params for you (so they can be typechecked).
TIP
openapi-fetch infers types from the URL. Prefer static string values over dynamic runtime values, e.g.:
- ✅
"/blogposts/{post_id}"
- ❌
[...pathParts].join("/") + "{post_id}"
This library also supports the label and matrix serialization styles as well (docs) automatically.
Request
The GET()
request shown needed the params
object that groups parameters by type (path
or query
). If a required param is missing, or the wrong type, a type error will be thrown.
The POST()
request required a body
object that provided all necessary requestBody data.
Response
All methods return an object with data, error, and response.
const { data, error, response } = await client.GET("/url");
Object | Response |
---|---|
data | 2xx response if OK; otherwise undefined |
error | 5xx , 4xx , or default response if not OK; otherwise undefined |
response | The original Response which contains status , headers , etc. |
Path-property style
If you prefer selecting the path as a property, you can create a path based client:
import { createPathBasedClient } from "openapi-fetch";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript
const client = createPathBasedClient<paths>({
baseUrl: "https://myapi.dev/v1",
});
client["/blogposts/{post_id}"].GET({
params: { post_id: "my-post" },
query: { version: 2 },
});
Note that this has performance implications and does not allow to attach middlewares directly. See wrapAsPathBasedClient
for more.
Support
Platform | Support |
---|---|
Browsers | See fetch API support (widely-available in all major browsers) |
Node | >= 18.0.0 |
TypeScript | >= 4.7 (>= 5.0 recommended) |