How to Generate a Typesafe REST API Client Using Swagger & Node.js
Written by Bahaa Zidan
Most backend frameworks nowadays come with OpenAPI specification support baked in or provide a very simple way to add it. All an OpenAPI spec (formerly Swagger spec) does is describe the REST API endpoints of your app. The types/shapes of request, response, headers, …etc.
Assuming our backend exposes this spec. We can use it in JavaScript land to generate a fully typesafe API client. This can work in any modern JS framework. I use Svelte Kit but the exact code can work in React & Next, Vue & Nuxt, or any other framework. All you need is Node.js and the ability to copy and paste.
Install required packages
pnpm add -D dotenv swagger-typescript-api valibot
Wherever you want, create a codegen-swagger.ts
file and paste the following code in
import "dotenv/config";
import path from "path";
import { generateApi } from "swagger-typescript-api";
import { nonEmpty, object, parse, pipe, string, url } from "valibot";
const envSchema = object({
API_BASE_URL: pipe(string(), url()),
API_TOKEN: pipe(string(), nonEmpty()),
});
const env = parse(envSchema, process.env);
const SWAGGER_SCHEMA_URL = `${env.API_BASE_URL}/swagger/doc.json`;
async function main() {
const response = await fetch(SWAGGER_SCHEMA_URL, {
headers: {
"X-API-Token": env.API_TOKEN,
},
});
const json = await response.json();
generateApi({
name: "api",
spec: json,
output: path.resolve(process.cwd(), "./src/lib/__generated__"),
});
}
await main();
Let’s break it down:
- We use
dotenv
to load the environment variables - We use
valibot
to verify the environment variables have the correct shape and type. Feel free to usezod
orjoi
here. I just prefervalibot
- Inside an async main function, we fetch the swagger schema. You can add any required credentials in this step.
- Lastly we use
swagger-typescript-api
’sgenerateApi
to generate the typesafe API client.
All that’s left is to add a script in package.json
{
"scripts": {
"codegen:swagger": "node --experimental-strip-types ./scripts/codegen-swagger.ts"
}
}
Note that in future Node.js versions you may not need the --experimental-strip-types
flag to run typescript files. I’m running Node version 22.13
and I need it here
Run the script and you should see the client generated in src/lib/__generated__
Here’s a simple demonstration:
Create an API instance
import { API_BASE_URL } from "$env/static/private";
import { Api } from "./__generated__/api";
export const api = new Api({
baseUrl: API_BASE_URL,
});
Use that instance to call any endpoint you want
import { API_TOKEN } from "$env/static/private";
import { api } from "$lib";
export async function companyDetails(id: string) {
const response = (
await api.company.companyDetail(id, {
headers: {
"X-API-Token": API_TOKEN,
},
})
).data.data;
return response;
}