my avatar Bahaa Zidan
Search
/

How to Generate a Typesafe REST API Client Using Swagger & Node.js

Published on How to Generate a Typesafe REST API Client Using Swagger & Node.js hero image

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:

  1. We use dotenv to load the environment variables
  2. We use valibot to verify the environment variables have the correct shape and type. Feel free to use zod or joi here. I just prefer valibot
  3. Inside an async main function, we fetch the swagger schema. You can add any required credentials in this step.
  4. Lastly we use swagger-typescript-api's generateApi 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;
}