npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zyno-io/openapi-client-codegen

v2.7.3

Published

OpenAPI client generator for TypeScript apps

Readme

@zyno-io/openapi-client-codegen

A TypeScript library that wraps @hey-api/openapi-ts to provide a batteries-included workflow for generating and consuming OpenAPI clients. It adds runtime client configuration, error handling, request middleware, and file upload support on top of the generated SDK.

Table of Contents

Why not just use @hey-api/openapi-ts directly?

@hey-api/openapi-ts is a powerful codegen tool, but it only generates code — it doesn't provide runtime utilities for configuring the generated client in an application. This library fills that gap:

  • Client configurationconfigureOpenApiClient() sets up error handling, dynamic headers, and request middleware in one call.
  • Structured errors — Responses are wrapped in OpenApiError with access to the request, response, and parsed body, rather than raw fetch errors.
  • Request middleware — A wrapper function lets you intercept every request for logging, auth token injection, retry logic, etc.
  • File uploads — Drop-in classes for browser (FileUploadRequest) and React Native (ReactNativeFileUploadRequest) file uploads that automatically convert to multipart/form-data.
  • CLI and watch mode — A CLI tool and programmatic API for generating clients, with file watching for automatic regeneration during development.

Installation

npm install @zyno-io/openapi-client-codegen
# or
yarn add @zyno-io/openapi-client-codegen

Generating a Client

CLI

# Generate from a single spec
npx generate-openapi-client ./api.openapi.yaml ./src/generated

# Generate from config file (openapi-specs.json)
npx generate-openapi-client

# Watch mode — regenerates on file changes
npx generate-openapi-client -w ./api.openapi.yaml ./src/generated
npx generate-openapi-client -w  # watch all configured specs

Config file

Create openapi-specs.json in your project root:

{
    "./specs/api.yaml": "./src/generated/api",
    "./specs/admin.yaml": {
        "path": "./src/generated/admin",
        "prefix": "Admin"
    }
}

The prefix option prepends to generated SDK class names (e.g. AdminUsersApi instead of UsersApi).

For development, create openapi-specs.dev.json to redirect spec paths to local files without modifying the main config:

{
    "./specs/api.yaml": "../backend/api.openapi.yaml"
}

The keys match the paths in openapi-specs.json; the values point to where the spec actually lives during development (e.g. a file generated by a locally running backend). When using watch mode, the generator watches the dev path instead of the committed path. After each successful generation, it automatically copies the dev spec back to the committed path so that spec changes show up in git diff and stay in sync with version control.

Programmatic API

import {
    generateOpenapiClient,
    generateConfiguredOpenapiClients,
    createWatchfulOpenapiClientGenerator
} from '@zyno-io/openapi-client-codegen/generator';

// Generate a single client
await generateOpenapiClient('./api.yaml', './src/generated');

// Generate all clients from openapi-specs.json
await generateConfiguredOpenapiClients();

// Watch a single spec and regenerate on changes
const watcher = createWatchfulOpenapiClientGenerator('./api.yaml', './src/generated');
// watcher.generate() — manually trigger
// watcher.close() — stop watching

Configuring the Generated Client

The generated SDK exports a client instance. Pass it to configureOpenApiClient() to set up error handling, headers, and middleware:

import { configureOpenApiClient } from '@zyno-io/openapi-client-codegen';
import { client } from './generated/client.gen';

configureOpenApiClient(client, {
    headers: {
        Authorization: `Bearer ${token}`
    },
    onError: (err, options) => {
        if (err instanceof OpenApiError && err.response.status === 401) {
            redirectToLogin();
            return null; // the request promise will never settle
        }
    }
});

Options

headers

Static headers, or a (sync/async) function that returns headers per-request:

// Static
configureOpenApiClient(client, {
    headers: { 'X-API-Key': 'abc123' }
});

// Dynamic
configureOpenApiClient(client, {
    headers: async request => ({
        Authorization: `Bearer ${await getToken()}`
    })
});

Set a header to null to remove it from the request.

onError

Called when the server returns an error response. Receives an OpenApiError and the request options:

configureOpenApiClient(client, {
    onError: (err, options) => {
        // Throw a different error
        if (err.response.status === 403) {
            throw new ForbiddenError(err.message);
        }

        // Return null to silently discard the error — the request promise
        // will never resolve or reject, so the caller hangs and does nothing.
        // Useful when you're redirecting the user away from the page entirely.
        if (err.response.status === 401) {
            redirectToLogin();
            return null;
        }

        // Return void to throw the original OpenApiError
    }
});

wrapper

A middleware function that wraps every request. Receives the request options and the original request function, and must return the result. Useful for logging, retry logic, or request queuing:

configureOpenApiClient(client, {
    wrapper: async (options, request) => {
        const start = performance.now();
        try {
            const result = await request(options);
            console.log(`${options.method} ${options.url} — ${Math.round(performance.now() - start)}ms`);
            return result;
        } catch (err) {
            console.error(`${options.method} ${options.url} failed`, err);
            throw err;
        }
    }
});

File Uploads

The upload classes provide client-side compatibility with Deepkit's UploadedFile type, in conjunction with OpenAPI specs generated by deepkit-openapi. When a Deepkit backend exposes UploadedFile parameters, these classes satisfy the generated TypeScript types and automatically convert the request to multipart/form-data:

import { FileUploadRequest, ReactNativeFileUploadRequest } from '@zyno-io/openapi-client-codegen';

// Browser — wrap a File or Blob
const pdfFile = new File([pdfBlob], 'report.pdf', { type: 'application/pdf' });
const result = dataFrom(
    await ReportsApi.postReportsUpload({
        body: {
            title: 'Q4 Report',
            pdf: new FileUploadRequest(pdfFile)
        }
    })
);

// React Native — pass a file URI
await ProfileApi.postProfileUploadPhoto({
    body: {
        photo: new ReactNativeFileUploadRequest({
            uri: photo.uri,
            name: 'photo.jpg',
            type: 'image/jpeg'
        })
    }
});

Response Utilities

import { dataFrom, dataFromAsync, type OpenApiDataType } from '@zyno-io/openapi-client-codegen';

// Extract .data from a response
const response = await PetsApi.listPets();
const pets = dataFrom(response);

// Extract .data from a promise
const pets = await dataFromAsync(PetsApi.listPets());

// Extract the data type from a response type
type Pet = OpenApiDataType<Awaited<ReturnType<typeof PetsApi.getPet>>>;

Error Handling

configureOpenApiClient sets throwOnError: true on the client. Failed requests throw an OpenApiError:

import { OpenApiError } from '@zyno-io/openapi-client-codegen';

try {
    await PetsApi.getPet({ path: { petId: '123' } });
} catch (err) {
    if (err instanceof OpenApiError) {
        console.error(err.message); // "Not Found (404)"
        console.error(err.response); // Response object
        console.error(err.body); // Parsed response body
        console.error(err.request); // Request object
    }
}

License

MIT