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/dist/_template.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/{main-BZrWxHPM.mjs → main-BiX2OIVV.js} +86 -14
- package/dist/main.cjs +570 -11
- package/dist/main.d.cts +5 -2
- package/dist/main.d.mts +5 -2
- package/dist/main.js +1 -1
- package/dist/main.mjs +572 -10
- package/package.json +22 -24
- package/readme.md +101 -78
- package/dist/cli.cjs +0 -18
- package/dist/cli.mjs +0 -16
- package/dist/main-BLaUzCCM.cjs +0 -585
- package/dist/main-BZrWxHPM.js +0 -581
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://
|
|
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
|
-
|
|
17
|
+
Turn your OpenAPI spec into a typed TypeScript client with one command.
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
npm install apigen-ts --save-dev
|
|
32
|
-
```
|
|
29
|
+
## Install
|
|
33
30
|
|
|
34
31
|
```sh
|
|
35
|
-
|
|
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
|
|
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
|
|
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
|
|
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) //
|
|
67
|
+
await api.pet.getPetById(1) // → Pet
|
|
71
68
|
|
|
72
69
|
// GET /pet/findByStatus?status=sold
|
|
73
|
-
await api.pet.findPetsByStatus({ status: "sold" }) //
|
|
70
|
+
await api.pet.findPetsByStatus({ status: "sold" }) // → Pet[]
|
|
74
71
|
|
|
75
|
-
// PUT /user/{username}
|
|
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({
|
|
81
|
+
const { token } = await api.auth.login({ username, password })
|
|
85
82
|
api.Config.headers = { Authorization: token }
|
|
86
83
|
|
|
87
|
-
await api.protectedRoute.get() //
|
|
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 //
|
|
95
|
+
const createdAt: Date = pet.createdAt // parsed from format=date-time string
|
|
99
96
|
```
|
|
100
97
|
|
|
101
|
-
### String
|
|
98
|
+
### String unions instead of enums
|
|
102
99
|
|
|
103
|
-
|
|
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
|
-
//
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
162
|
+
await api.pet.getPetById(404)
|
|
128
163
|
} catch (e) {
|
|
129
|
-
console.log(e) //
|
|
164
|
+
console.log(e) // awaited response.json()
|
|
130
165
|
}
|
|
131
166
|
```
|
|
132
167
|
|
|
133
|
-
|
|
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
|
|
178
|
+
### Base URL resolving
|
|
152
179
|
|
|
153
|
-
|
|
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/")
|
|
156
|
-
- `new URL("v2/cats", "https://example.com/v1/")
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
183
|
-
name: "MyApiClient", // default "ApiClient"
|
|
184
|
-
parseDates: true, // default false
|
|
185
|
-
inlineEnums: false, // default false
|
|
186
|
-
|
|
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 [
|
|
189
|
-
if (proposal[0] === "users") return //
|
|
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) //
|
|
192
|
-
return [a, `${op.method}_${b}`] //
|
|
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
|
-
|
|
225
|
+
## Usage with FastAPI
|
|
198
226
|
|
|
199
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
## Compare
|
|
247
|
+
## Alternatives
|
|
227
248
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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();
|