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

apigen-ts

v1.3.0

Published

Simple typed OpenAPI client generator

Downloads

2,800

Readme

apigen-ts

Turn your OpenAPI spec into a typed TypeScript client with one command.

  • One file. Outputs a single api-client.ts — no scattered modules, no runtime deps in generated code.
  • Fully typed. Every method returns the exact response type from your schema. No casting, no any.
  • Pure Node.js. No Java, no Docker. Works with npx in any project.
  • Fetch-based. Uses native fetch. Override it with your own function for auth, retries, or logging.
  • All OpenAPI versions. Supports v2 (Swagger), v3, and v3.1 — auto-upgrades v2 on the fly.
  • Extras built in. Automatic date parsing, string literal unions instead of enums, Prettier formatting.
  • Filterable. Include or exclude endpoints by path regex or tag — essential for large schemas.

Unlike openapi-typescript, it generates a ready-to-call client — not just types. Unlike openapi-generator-cli, it's pure Node.js with zero Java dependency. Unlike openapi-typescript-codegen, it outputs a single file.

Install

npm i apigen-ts --save-dev

Usage

1. Generate

# From a local file
npx apigen-ts ./openapi.json ./api-client.ts

# From a URL
npx apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts

# From a protected URL
npx apigen-ts https://secret-api.example.com ./api-client.ts -H "x-api-key: secret-key"

Run npx apigen-ts --help for all options. See generated examples.

2. Import

import { ApiClient } from "./api-client"

const api = new ApiClient({
  baseUrl: "https://example.com/api",
  headers: { Authorization: "secret-token" },
})

3. Use

// GET /pet/{petId}
await api.pet.getPetById(1) // → Pet

// GET /pet/findByStatus?status=sold
await api.pet.findPetsByStatus({ status: "sold" }) // → Pet[]

// PUT /user/{username} — second arg is typed request body
await api.user.updateUser("username", { firstName: "John" })

Advanced

Login flow

const { token } = await api.auth.login({ username, password })
api.Config.headers = { Authorization: token }

await api.protectedRoute.get() // authenticated

Automatic date parsing

npx apigen-ts ./openapi.json ./api-client.ts --parse-dates
const pet = await api.pet.getPetById(1)
const createdAt: Date = pet.createdAt // parsed from format=date-time string

String unions instead of enums

Pass --inline-enums to generate string literal unions — useful for Node.js type stripping:

npx apigen-ts ./openapi.json ./api-client.ts --inline-enums
// Generated:
type MyEnum = "OptionA" | "OptionB"

// Instead of:
enum MyEnum {
  OptionA = "OptionA",
  OptionB = "OptionB",
}

Filter by path

Include only the endpoints you need — useful with large schemas (e.g. Cloudflare's 8 MB monolith):

npx apigen-ts ./openapi.json ./api-client.ts --filter-paths '^/accounts'

Filter by tag

# include only endpoints tagged "pets" or "store"
npx apigen-ts ./openapi.json ./api-client.ts --include-tags pets,store

# exclude endpoints tagged "internal"
npx apigen-ts ./openapi.json ./api-client.ts --exclude-tags internal

When both flags are set, --exclude-tags wins.

AbortController / cancellation

Pass --fetch-options to add an optional last argument to every generated method, accepting any RequestInit field (including signal):

npx apigen-ts ./openapi.json ./api-client.ts --fetch-options
const controller = new AbortController()
await api.pet.getPetById(1, { signal: controller.signal })

// cancel the request
controller.abort()

Error handling

Non-2xx responses throw — the caught value is the parsed response body:

try {
  await api.pet.getPetById(404)
} catch (e) {
  console.log(e) // awaited response.json()
}

Override ParseError to control the shape:

class MyClient extends ApiClient {
  async ParseError(rep: Response) {
    return { code: "API_ERROR" }
  }
}

Base URL resolving

By default uses the URL constructor: new URL(path, baseUrl). Notable behavior:

  • new URL("/v2/cats", "https://example.com/v1/")https://example.com/v2/cats
  • new URL("v2/cats", "https://example.com/v1/")https://example.com/v1/v2/cats

Override PrepareFetchUrl to change this (see #2):

class MyClient extends ApiClient {
  PrepareFetchUrl(path: string) {
    return new URL(`${this.Config.baseUrl}/${path}`.replace(/\/{2,}/g, "/"))
  }
}

const api = new MyClient({ baseUrl: "https://example.com/v1" })
await api.pet.getPetById(1) // → https://example.com/v1/pet/1

Node.js API

import { apigen } from "apigen-ts"

await apigen({
  source: "https://petstore3.swagger.io/api/v3/openapi.json",
  output: "./api-client.ts",
  // optional:
  name: "MyApiClient", // default: "ApiClient"
  parseDates: true, // default: false
  inlineEnums: false, // default: false
  fetchOptions: true, // default: false
  filterPaths: /^\/pets/, // only include paths matching regex
  includeTags: ["pets", "store"], // only include these tags
  excludeTags: ["internal"], // exclude these tags (wins over includeTags)
  headers: { "x-api-key": "secret-key" },
  resolveName(ctx, op, proposal) {
    // proposal is [namespace, methodName]
    if (proposal[0] === "users") return // use default

    const [a, b] = op.name.split("/").slice(3, 5) // /api/v1/store/items/search
    return [a, `${op.method}_${b}`] // → api.store.get_items()
  },
})

Usage with FastAPI

By default, FastAPI generates verbose operationIds. Fix with a custom resolver:

from fastapi import FastAPI
from fastapi.routing import APIRoute

app = FastAPI()

# add your routes here

def update_operation_ids(app: FastAPI) -> None:
    for route in app.routes:
        if isinstance(route, APIRoute):
            ns = route.tags[0] if route.tags else "general"
            route.operation_id = f"{ns}_{route.name}".lower()

# call after all routes are added
update_operation_ids(app)

Alternatives

| Package | Issue | | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | openapi-typescript-codegen | No single-file output (#1263) | | openapi-typescript | Low-level types only — no callable client, no named type exports | | openapi-generator-cli | Wraps a Java library | | swagger-typescript-api | Complex config, breaking API changes between versions |

Development

  • https://ts-ast-viewer.com
  • https://jsonschemalint.com
  • https://redocly.github.io/redoc/
  • https://swagger.io/docs/specification/basic-structure/