Differences from the original
swagger-typescript-api-es is based on
acacode/swagger-typescript-api and generates
the same kind of client, with these differences:
- ESM only, written in TypeScript. There is no CommonJS build.
- Config file support. The CLI loads
swagger-typescript-api.config.ts(or.js/.mjs/.cjs) from the project root. The file can export one config or an array of configs, anddefaultConfig()types it for you. - The CLI flags are different. Flags are
--u,--o,--n,--t, ... (for example-u <url>rather than upstream's-p <path>). Flags that take a value need one, so booleans are written as--modular true. See CLI. - oxfmt formats the output, not Prettier. Set
formatter options with
oxfmtOptrions(that spelling is the real option name) or with your project's.oxfmtrc.json. output: falsegenerates in memory and returns the files fromgenerateApiwithout writing them.- The generated
ContentTypeis aconstobject plus a type, not a TypeScriptenum. Both generated HTTP clients also accept aninjectHeaderscallback. - There is no
generate-templatesCLI command. To copy the built-in templates, callgenerateTemplates()from code.
Performance
Generation time compared with the original swagger-typescript-api v13.13.0 (median, warm process,
same documents and options, Node.js v22.22.2 on 4 cores):
A single cold run (new process, like the CLI) of the largest document takes about
938 ms instead of 13.1 s, with less than half the memory. All numbers, the method and
the caveats are on the docs site's Benchmark page; run it yourself with npm run bench:compare.
Requirements
- Node.js >= 20
- ESM. The package is
"type": "module", so import it withimport, notrequire. - If you use
httpClientType: 'axios',axiosmust be installed in the project that uses the generated client.
Install
To run it once without installing:
Quick start
One-liner. This writes ./src/api/Api.ts:
Config file. Create swagger-typescript-api.config.ts in the project root:
Then add a script to package.json and run it with npm run gen-api-types:
Config file
The CLI looks for a config file in the current working directory (with
unreadconfig). The usual name is:
swagger-typescript-api.config.ts,.js,.mjsor.cjs
It also finds .swagger-typescript-apirc in any of these forms: bare, .json, .json5, .jsonc,
.yaml, .yml, .toml, .ini, .js, .ts, .cjs, .mjs, or the same names under .config/.
These rc files are checked before swagger-typescript-api.config.*. As a last resort, the CLI
reads a "swagger-typescript-api" key in package.json. If none of these exist, the CLI needs
--u (or --custom-config).
defaultConfig() only returns what you pass it. It exists so that the config is typed. The file can
export a single config or an array of configs. Each config in an array is generated in turn:
Precedence, from lowest to highest:
- The config file
- The file passed with
--custom-config - CLI flags
When the config file exports an array, the CLI flags and the --custom-config options are applied
on top of every entry.
CLI
- You can write flags in kebab-case or camelCase:
--union-enums trueand--unionEnums trueare the same flag. Single-letter flags also work with one dash (-u,-o,-n,-t,-d,-r). - Boolean flags need a value. Write
--modular trueor--modular false.false,0,no,offand an empty string count asfalse; anything else counts astrue. A bare--modularfails withoption '--modular <modular>' value is missing.--extract-enumsis the only exception: it is a plain switch. --module-name-indexmust be a number.- The exit code is
0on success and1on any error. The error is printed as❌ SWAGGER-TYPESCRIPT-API error: <message>. -h, --helpprints the help and-v, --versionprints the version.
Many options have no flag, for example hooks, requestOptions, oxfmtOptrions, input and
extractingOptions. Set those in the config file or in --custom-config.
Options reference
These are all the fields of IOptions, which defaultConfig() and generateApi() accept.
Relative paths resolve from process.cwd().
Input / output
Where the schema comes from, in order: spec, then input (if the file exists), then url.
Client
Types and enums
Extraction
<a id="extractingoptions-defaults"></a>Defaults of extractingOptions:
A request body is optional in the generated method unless the schema marks it required: true.
Routes and modules
Routes whose path has no module segment (for example GET /) become methods directly on the Api
class. In modular mode they go to Common.ts.
Output formatting
Network (schema download)
Logging
Advanced
Example: map date-time strings to Date and int64 integers to bigint.
Programmatic API
generateApi(options)
It returns a Promise of:
If you pass an array of options, it runs them one after another and resolves with an array of results.
generateApi rejects on errors: the schema is missing or invalid, the download fails, output is
an existing file, and so on.
All public types are exported from the package entry: IOptions, Hooks, GenerateApiOutput,
GeneratedFile, OpenAPIDocument, SchemaObject, ParsedRoute, ...
To pass a schema object you already have, use spec (typed as OpenAPIDocument, OpenAPI 3.x or
Swagger 2.0). The object is never modified by the generator, and it is not even copied unless hooks,
custom schema parsers, templates or type constructs are used:
generateTemplates(params)
This copies the built-in templates (.ejs) into a folder so you can customize them. See
Custom templates.
defaultConfig(options)
Returns options unchanged. It exists to type the config file.
constants
The built-in constants (HTTP_CLIENT, SCHEMA_TYPES, OXC_FORMAT_OPTIONS, TEMPLATES_DIR,
RESERVED_*_ARG_NAMES, ...).
Using the generated client
The examples below come from a single-file client with the default apiClassName: 'Api'. The
method names come from operationId (camelCased). Without one, the name is built from the HTTP
method and the path.
fetch (default)
- With
disableThrowOnError: true, failed responses resolve instead of rejecting. Checkresponse.okandresponse.erroryourself. - With
unwrapResponseData: true, methods resolve with the body (Promise<Pet>). A failed request still rejects with theHttpResponse. injectHeaders: (headers) => headers | Promise<headers>in the constructor config can rewrite the headers of every request.
axios
Generate with httpClientType: 'axios' (and install axios):
With unwrapResponseData: true, methods resolve with response.data directly. Cancel requests
with axios' own signal option: api.pets.getPet(1, { signal: controller.signal }).
singleHttpClient
Modular output
With modular: true, output gets several files instead of one (name is ignored):
- Module files are named
PascalCase(moduleName).ts, and each exports a class of the same name. If that name clashes with a data contract, the class is named<Module>Apiinstead. - Route type files are
<Module>Route.ts. They exportnamespace <Module>, ornamespace <Module>Routeon a clash. - With
generateClient: false, onlydata-contracts.ts(and the route types) are written. - With
toJS: true, every file becomes.js+.d.ts.
In single-file mode, generateRouteTypes: true adds the route namespaces to the same file.
Custom templates
Templates use Eta v2 syntax (<%~ raw %>, <%= escaped %>,
<% code %>, data in it). Files may end in .eta or .ejs.
1. Copy the built-in templates:
The copied files are flattened into one folder. Their @base/, @default/ and @modular/
include paths are rewritten to ./.
2. Edit the files you need, and delete the rest. A template missing from your folder falls back to the built-in one.
3. Point the generator at the folder: templates: './api-templates' or
--t ./api-templates.
The partials they include are procedure-call.ejs (one API method), route-docs.ejs (JSDoc of a
method), route-type.ejs (one route namespace) and http-clients/{fetch,axios}-http-client.ejs.
How templates are found: your templates folder first, then templates/base, then
templates/default or templates/modular. Inside a template, includeFile(path, data) accepts the
prefixes @base/, @default/, @modular/, @original/ and @custom/. A path without a prefix
is looked up in the custom folder, then in the built-in one.
What it contains: apiConfig (baseUrl, title, version, info, servers, tags, ...),
config (all options, including config.Ts constructs and config.constants), modelTypes,
routes ({ outOfModule, combined }), route (the current module, in modular api and
routeTypes), hasSecurityRoutes, hasQueryRoutes, hasFormDataRoutes, generateResponses,
fileName, translateToJavaScript and utils.
utils: Ts, formatDescription, escapeJSDocContent, internalCase, classNameCase,
pascalCase, getInlineParseContent, getParseContent, getComponentByRef, parseSchema,
checkAndAddNull, safeAddNullToType, isNeedToAddNull, inlineExtraFormatters, formatters,
formatModelName, fmtToJSDocLine, NameResolver, require, and _, lodash-compatible helpers
(from es-toolkit/compat):
compact, merge, each, isEmpty, sortByProperty, noop, isObject, isString,
isUndefined, map, uniq, size, replace, camelCase, lowerCase, values, join, get,
upperCase, sortBy. require('./x') resolves from the templates folder. A package name
resolves from your project first.
Extra files: extraTemplates: [{ name: 'endpoints.ts', path: './tpl/endpoints.ejs' }] renders
each template with the same data and writes it next to the other files.
Hooks
Set hooks under hooks in the config file (or --custom-config) or in generateApi options. When a
hook returns undefined, the original value is kept.
All hooks are typed (Hooks is exported), so a .ts config gets autocompletion and type checking
for their arguments and return values.
rawRouteInfo / routeData.raw contains the operation fields (operationId, method, route,
moduleName, tags, summary, description, responses, requestBody, pathArgs, ...).
Formatting
Generated code is formatted with oxfmt. Options are merged in this order, and later ones win:
- Built-in defaults:
singleQuote: true,jsxSingleQuote: true,printWidth: 100,trailingComma: 'es5',tabWidth: 2,semi: true, and groupedsortImports .oxfmtrc.jsonin the current working directory, if there is one (its$schema,ignorePatternsandoverridesare ignored)oxfmtOptrionsfrom your config. The spelling is intentional.
If formatting fails, a warning is printed and the code is written unformatted.
Troubleshooting / FAQ
- The CLI exits with code
1. Every error is printed as❌ SWAGGER-TYPESCRIPT-API error: ...and the process exits with1, so a failed generation also fails CI. option '--xxx <xxx>' value is missing. Boolean flags need a value:--modular true.missing swagger schema. Pass--u <url>or createswagger-typescript-api.config.tscontainingurl,inputorspec. Every entry of an array config needs one.- Local file or URL?
urltreats anything that does not start withhttp://orhttps://as a path relative to the working directory.inputis always a local path. JSON and YAML both work. Output path "..." is not a directory.outputmust be a directory. Set the file name withname/--n.silentstill prints something.silenthides logs, but errors are always printed.- Download timeouts. The schema download times out after 60 s. Change it with
requestOptions: { timeout: 120_000 }(0disables the timeout). This option has no CLI flag. - Self-signed certificates. Use
disableStrictSSL: true(or--disableStrictSSL true). - Proxies. The download respects
HTTP_PROXY/HTTPS_PROXY/NO_PROXY. UsedisableProxy: trueto connect directly, or pass your own undici dispatcher:requestOptions: { dispatcher: new ProxyAgent(proxyUrl) }(import { ProxyAgent } from 'undici'). The node-fetch styleagentoption is no longer supported. - Protected schema. Use
authorizationToken: 'Bearer <token>'orrequestOptions: { headers: { ... } }. sortRoutersis deprecated. UsesortRoutes.- Why is the body or params argument optional? A request body is optional unless the schema marks it
required: true. WithextractRequestParams, the params argument is optional when all its fields are optional. require is not definedorERR_REQUIRE_ESM. The package is ESM only. Useimport(and"type": "module"or.mjs/.tsfor your own scripts).
AI agent skill
Contributing
Tests live in tests/:
tests/core: CLI, configuration, option parsing, schema resolving and download,generateApiandgenerateTemplatestests/generation: generated output (snapshots, naming, parameters, responses, types)tests/options: behaviour of individual optionstests/runtime: runs the generated fetch and axios clients against real requeststests/fixturesandtests/helpers: shared schemas and helpers
To run a single file: npx vitest run tests/core/cli-start.test.ts.
Docs site and online playground
docs/ is a Next.js app with the documentation (rendered from this README) and a playground that
generates clients in a serverless function (POST /api/generate), so you can try options, view the
generated files and download them (one by one or as a .zip).
See docs/README.md for the API, its security limits and how to deploy it on Vercel.
CI runs lint, fmt:check, typecheck, the tests and the build on Node 20 and 22. Commit messages
follow Conventional Commits (feat:, fix:, docs:,
chore:, ...).
License
Licensed under the MIT License.
Based on swagger-typescript-api by acacode (MIT). Rewritten to TypeScript and ES modules by hunghg255.