apigen-ts 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <div align="center">
4
4
 
5
5
  [<img src="https://badges.ws/npm/v/apigen-ts" alt="version" />](https://npmjs.org/package/apigen-ts)
6
- [<img src="https://badges.ws/packagephobia/publish/apigen-ts" alt="size" />](https://packagephobia.now.sh/result?p=apigen-ts)
6
+ [<img src="https://packagephobia.com/badge?p=array-utils-ts" alt="size" />](https://packagephobia.now.sh/result?p=apigen-ts)
7
7
  [<img src="https://badges.ws/npm/dm/apigen-ts" alt="downloads" />](https://npmjs.org/package/apigen-ts)
8
8
  [<img src="https://badges.ws/github/license/vladkens/apigen-ts" alt="license" />](https://github.com/vladkens/apigen-ts/blob/main/LICENSE)
9
9
  [<img src="https://badges.ws/badge/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee&label" alt="donate" />](https://buymeacoffee.com/vladkens)
@@ -14,25 +14,22 @@
14
14
  <img src="./logo.svg" alt="apigen-ts logo" height="80" />
15
15
  </div>
16
16
 
17
- ## Features
17
+ Turn your OpenAPI spec into a typed TypeScript client with one command.
18
18
 
19
- - Generates ready to use `ApiClient` with types (using `fetch`)
20
- - Single output file, minimal third-party code
21
- - Loads schemas from JSON / YAML, locally and remote
22
- - Ability to customize `fetch` with your custom function
23
- - Automatic formating with Prettier
24
- - Can parse dates from date-time format (`--parse-dates` flag)
25
- - Support OpenAPI v2, v3, v3.1
26
- - Can be used with npx as well
19
+ - **One file.** Outputs a single `api-client.ts` no scattered modules, no runtime deps in generated code.
20
+ - **Fully typed.** Every method returns the exact response type from your schema. No casting, no `any`.
21
+ - **Pure Node.js.** No Java, no Docker. Works with `npx` in any project.
22
+ - **Fetch-based.** Uses native `fetch`. Override it with your own function for auth, retries, or logging.
23
+ - **All OpenAPI versions.** Supports v2 (Swagger), v3, and v3.1 — auto-upgrades v2 on the fly.
24
+ - **Extras built in.** Automatic date parsing, string literal unions instead of enums, Prettier formatting.
25
+ - **Filterable.** Include or exclude endpoints by path regex or tag — essential for large schemas.
27
26
 
28
- ## Install
27
+ 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.
29
28
 
30
- ```sh
31
- npm install apigen-ts --save-dev
32
- ```
29
+ ## Install
33
30
 
34
31
  ```sh
35
- yarn add -D apigen-ts
32
+ npm i -D apigen-ts
36
33
  ```
37
34
 
38
35
  ## Usage
@@ -40,17 +37,17 @@ yarn add -D apigen-ts
40
37
  ### 1. Generate
41
38
 
42
39
  ```sh
43
- # From file
40
+ # From a local file
44
41
  npx apigen-ts ./openapi.json ./api-client.ts
45
42
 
46
- # From url
43
+ # From a URL
47
44
  npx apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts
48
45
 
49
- # From protected url
46
+ # From a protected URL
50
47
  npx apigen-ts https://secret-api.example.com ./api-client.ts -H "x-api-key: secret-key"
51
48
  ```
52
49
 
53
- Run `npx apigen-ts --help` for more options. Examples of generated clients [here](./examples/).
50
+ Run `npx apigen-ts --help` for all options. See [generated examples](./examples/).
54
51
 
55
52
  ### 2. Import
56
53
 
@@ -67,12 +64,12 @@ const api = new ApiClient({
67
64
 
68
65
  ```ts
69
66
  // GET /pet/{petId}
70
- await api.pet.getPetById(1) // -> Pet
67
+ await api.pet.getPetById(1) // Pet
71
68
 
72
69
  // GET /pet/findByStatus?status=sold
73
- await api.pet.findPetsByStatus({ status: "sold" }) // -> Pets[]
70
+ await api.pet.findPetsByStatus({ status: "sold" }) // Pet[]
74
71
 
75
- // PUT /user/{username}; second arg body with type User
72
+ // PUT /user/{username} second arg is typed request body
76
73
  await api.user.updateUser("username", { firstName: "John" })
77
74
  ```
78
75
 
@@ -81,10 +78,10 @@ await api.user.updateUser("username", { firstName: "John" })
81
78
  ### Login flow
82
79
 
83
80
  ```ts
84
- const { token } = await api.auth.login({ usename, password })
81
+ const { token } = await api.auth.login({ username, password })
85
82
  api.Config.headers = { Authorization: token }
86
83
 
87
- await api.protectedRoute.get() // here authenticated
84
+ await api.protectedRoute.get() // authenticated
88
85
  ```
89
86
 
90
87
  ### Automatic date parsing
@@ -95,67 +92,97 @@ npx apigen-ts ./openapi.json ./api-client.ts --parse-dates
95
92
 
96
93
  ```ts
97
94
  const pet = await api.pet.getPetById(1)
98
- const createdAt: Date = pet.createdAt // date parsed from string with format=date-time
95
+ const createdAt: Date = pet.createdAt // parsed from format=date-time string
99
96
  ```
100
97
 
101
- ### String union as enums
98
+ ### String unions instead of enums
102
99
 
103
- You can generate string literal union instead of native enums in case you want to run in Node.js environment with [type-stripping](https://nodejs.org/api/typescript.html#type-stripping). To achive this pass `--inline-enums` command line argument or use `inlineEnums: true` in Node.js API.
100
+ Pass `--inline-enums` to generate string literal unions useful for Node.js [type stripping](https://nodejs.org/api/typescript.html#type-stripping):
104
101
 
105
102
  ```sh
106
103
  npx apigen-ts ./openapi.json ./api-client.ts --inline-enums
107
104
  ```
108
105
 
109
- This will generate:
110
-
111
106
  ```ts
107
+ // Generated:
112
108
  type MyEnum = "OptionA" | "OptionB"
113
109
 
114
- // instead of
115
- enum MyEnum = {
110
+ // Instead of:
111
+ enum MyEnum {
116
112
  OptionA = "OptionA",
117
- OptionB = "OptionB"
113
+ OptionB = "OptionB",
118
114
  }
119
115
  ```
120
116
 
121
- ### Errors handling
117
+ ### Filter by path
118
+
119
+ Include only the endpoints you need — useful with large schemas (e.g. Cloudflare's 8 MB monolith):
120
+
121
+ ```sh
122
+ npx apigen-ts ./openapi.json ./api-client.ts --filter-paths '^/accounts'
123
+ ```
124
+
125
+ ### Filter by tag
126
+
127
+ ```sh
128
+ # include only endpoints tagged "pets" or "store"
129
+ npx apigen-ts ./openapi.json ./api-client.ts --include-tags pets,store
130
+
131
+ # exclude endpoints tagged "internal"
132
+ npx apigen-ts ./openapi.json ./api-client.ts --exclude-tags internal
133
+ ```
122
134
 
123
- An exception will be thrown for all unsuccessful return codes.
135
+ When both flags are set, `--exclude-tags` wins.
136
+
137
+ ### Fetch options
138
+
139
+ Pass `--fetch-options` to add an optional last argument to every generated method, accepting any [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) field:
140
+
141
+ ```sh
142
+ npx apigen-ts ./openapi.json ./api-client.ts --fetch-options
143
+ ```
144
+
145
+ ```ts
146
+ const controller = new AbortController()
147
+ await api.pet.getPetById(1, { signal: controller.signal })
148
+
149
+ // cancel the request
150
+ controller.abort()
151
+
152
+ // or disable automatic redirects for a single request
153
+ await api.pet.getPetById(1, { redirect: "manual" })
154
+ ```
155
+
156
+ ### Error handling
157
+
158
+ Non-2xx responses throw — the caught value is the parsed response body:
124
159
 
125
160
  ```ts
126
161
  try {
127
- const pet = await api.pet.getPetById(404)
162
+ await api.pet.getPetById(404)
128
163
  } catch (e) {
129
- console.log(e) // parse error depend of your domain model, e is awaited response.json()
164
+ console.log(e) // awaited response.json()
130
165
  }
131
166
  ```
132
167
 
133
- Also you can define custom function to parse error:
168
+ Override `ParseError` to control the shape:
134
169
 
135
170
  ```ts
136
171
  class MyClient extends ApiClient {
137
172
  async ParseError(rep: Response) {
138
- // do what you want
139
173
  return { code: "API_ERROR" }
140
174
  }
141
175
  }
142
-
143
- try {
144
- const api = new MyClient()
145
- const pet = await api.pet.getPetById(404)
146
- } catch (e) {
147
- console.log(e) // e is { code: "API_ERROR" }
148
- }
149
176
  ```
150
177
 
151
- ### Base url resolving
178
+ ### Base URL resolving
152
179
 
153
- You can modify how the endpoint url is created. By default [URL constructor](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) used to resolve endpoint url like: `new URL(path, baseUrl)` which has specific resolving [rules](https://developer.mozilla.org/en-US/docs/Web/API/URL_API/Resolving_relative_references). E.g.:
180
+ By default uses the [URL constructor](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL): `new URL(path, baseUrl)`. Notable behavior:
154
181
 
155
- - `new URL("/v2/cats", "https://example.com/v1/") // -> https://example.com/v2/cats`
156
- - `new URL("v2/cats", "https://example.com/v1/") // -> https://example.com/v1/v2/cats`
182
+ - `new URL("/v2/cats", "https://example.com/v1/")` `https://example.com/v2/cats`
183
+ - `new URL("v2/cats", "https://example.com/v1/")` `https://example.com/v1/v2/cats`
157
184
 
158
- If you want to have custom endpoint url resolving rules, you can override `PrepareFetchUrl` method. For more details see [issue](https://github.com/vladkens/apigen-ts/issues/2).
185
+ Override `PrepareFetchUrl` to change this (see [#2](https://github.com/vladkens/apigen-ts/issues/2)):
159
186
 
160
187
  ```ts
161
188
  class MyClient extends ApiClient {
@@ -165,42 +192,39 @@ class MyClient extends ApiClient {
165
192
  }
166
193
 
167
194
  const api = new MyClient({ baseUrl: "https://example.com/v1" })
168
- // will call: https://example.com/v1/pet/ instead of https://example.com/pet/
169
- const pet = await api.pet.getPetById(404)
195
+ await api.pet.getPetById(1) // https://example.com/v1/pet/1
170
196
  ```
171
197
 
172
198
  ### Node.js API
173
199
 
174
- Create file like `apigen.mjs` with content:
175
-
176
200
  ```js
177
201
  import { apigen } from "apigen-ts"
178
202
 
179
203
  await apigen({
180
204
  source: "https://petstore3.swagger.io/api/v3/openapi.json",
181
205
  output: "./api-client.ts",
182
- // everything below is optional
183
- name: "MyApiClient", // default "ApiClient"
184
- parseDates: true, // default false
185
- inlineEnums: false, // default false, use string literal union instead of enum
186
- headers: { "x-api-key": "secret-key" }, // Custom HTTP headers to use when fetching schema
206
+ // optional:
207
+ name: "MyApiClient", // default: "ApiClient"
208
+ parseDates: true, // default: false
209
+ inlineEnums: false, // default: false
210
+ fetchOptions: true, // default: false
211
+ filterPaths: /^\/pets/, // only include paths matching regex
212
+ includeTags: ["pets", "store"], // only include these tags
213
+ excludeTags: ["internal"], // exclude these tags (wins over includeTags)
214
+ headers: { "x-api-key": "secret-key" },
187
215
  resolveName(ctx, op, proposal) {
188
- // proposal is [string, string] which represents module.funcName
189
- if (proposal[0] === "users") return // will use default proposal
216
+ // proposal is [namespace, methodName]
217
+ if (proposal[0] === "users") return // use default
190
218
 
191
- const [a, b] = op.name.split("/").slice(3, 5) // eg. /api/v1/store/items/search
192
- return [a, `${op.method}_${b}`] // [store, 'get_items'] -> apiClient.store.get_items()
219
+ const [a, b] = op.name.split("/").slice(3, 5) // /api/v1/store/items/search
220
+ return [a, `${op.method}_${b}`] // api.store.get_items()
193
221
  },
194
222
  })
195
223
  ```
196
224
 
197
- Then run with: `node apigen.mjs`
225
+ ## Usage with FastAPI
198
226
 
199
- ## Usage with different backend frameworks
200
-
201
- ### FastAPI
202
-
203
- By default `apigen-ts` generates noisy method names when used with FastAPI. This can be fixed by [custom resolving](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) for operations ids.
227
+ By default, FastAPI generates verbose `operationId`s. Fix with a custom resolver:
204
228
 
205
229
  ```py
206
230
  from fastapi import FastAPI
@@ -216,19 +240,18 @@ def update_operation_ids(app: FastAPI) -> None:
216
240
  ns = route.tags[0] if route.tags else "general"
217
241
  route.operation_id = f"{ns}_{route.name}".lower()
218
242
 
219
-
220
- # this function should be after all routes added
243
+ # call after all routes are added
221
244
  update_operation_ids(app)
222
245
  ```
223
246
 
224
- _Note: If you want FastAPI to be added as preset, open PR please._
225
-
226
- ## Compare
247
+ ## Alternatives
227
248
 
228
- - [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) ([npm](https://www.npmjs.com/package/openapi-typescript-codegen)): no single file mode [#1263](https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1263#issuecomment-1502890838)
229
- - [openapi-typescript](https://github.com/drwpow/openapi-typescript) ([npm](https://www.npmjs.com/package/openapi-typescript)): low level api; no named types export to use in client code
230
- - [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli) ([npm](https://www.npmjs.com/package/@openapitools/openapi-generator-cli)): wrapper around java lib
231
- - [swagger-typescript-api](https://github.com/acacode/swagger-typescript-api) ([npm](https://www.npmjs.com/package/swagger-typescript-api)): complicated configuration; user-api breaking changes between versions
249
+ | Package | Issue |
250
+ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
251
+ | [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) | No single-file output ([#1263](https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1263#issuecomment-1502890838)) |
252
+ | [openapi-typescript](https://github.com/drwpow/openapi-typescript) | Low-level types only no callable client, no named type exports |
253
+ | [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli) | Wraps a Java library |
254
+ | [swagger-typescript-api](https://github.com/acacode/swagger-typescript-api) | Complex config, breaking API changes between versions |
232
255
 
233
256
  ## Development
234
257
 
package/dist/cli.cjs DELETED
@@ -1,18 +0,0 @@
1
- 'use strict';
2
-
3
- var main$1 = require('./main-BLaUzCCM.cjs');
4
- require('fs/promises');
5
- require('path');
6
- require('url');
7
- require('cleye');
8
- require('@redocly/openapi-core');
9
- require('array-utils-ts');
10
- require('lodash-es');
11
- require('swagger2openapi');
12
- require('typescript');
13
- require('node:path');
14
-
15
- const main = async () => {
16
- await main$1.apigen(main$1.getCliConfig());
17
- };
18
- main();
package/dist/cli.mjs DELETED
@@ -1,16 +0,0 @@
1
- import { a as apigen, g as getCliConfig } from './main-BZrWxHPM.mjs';
2
- import 'fs/promises';
3
- import 'path';
4
- import 'url';
5
- import 'cleye';
6
- import '@redocly/openapi-core';
7
- import 'array-utils-ts';
8
- import 'lodash-es';
9
- import 'swagger2openapi';
10
- import 'typescript';
11
- import 'node:path';
12
-
13
- const main = async () => {
14
- await apigen(getCliConfig());
15
- };
16
- main();