apigen-ts 1.2.0 → 1.3.0
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-l0LIDQ3K.mjs → main-B8jUVaQq.js} +91 -14
- package/dist/main.cjs +570 -11
- package/dist/main.d.cts +4 -0
- package/dist/main.d.mts +4 -0
- package/dist/main.js +1 -1
- package/dist/main.mjs +572 -10
- package/package.json +16 -18
- package/readme.md +103 -83
- package/dist/cli.cjs +0 -18
- package/dist/cli.mjs +0 -16
- package/dist/main-C0qK6dZX.cjs +0 -580
- package/dist/main-l0LIDQ3K.js +0 -576
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 apigen-ts --save-dev
|
|
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
|
|
44
|
-
|
|
40
|
+
# From a local file
|
|
41
|
+
npx apigen-ts ./openapi.json ./api-client.ts
|
|
45
42
|
|
|
46
|
-
# From
|
|
47
|
-
|
|
43
|
+
# From a URL
|
|
44
|
+
npx apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts
|
|
48
45
|
|
|
49
|
-
# From protected
|
|
50
|
-
|
|
46
|
+
# From a protected URL
|
|
47
|
+
npx apigen-ts https://secret-api.example.com ./api-client.ts -H "x-api-key: secret-key"
|
|
51
48
|
```
|
|
52
49
|
|
|
53
|
-
Run `
|
|
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,81 +78,108 @@ 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
|
|
91
88
|
|
|
92
89
|
```sh
|
|
93
|
-
|
|
90
|
+
npx apigen-ts ./openapi.json ./api-client.ts --parse-dates
|
|
94
91
|
```
|
|
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
|
+
```
|
|
134
|
+
|
|
135
|
+
When both flags are set, `--exclude-tags` wins.
|
|
136
|
+
|
|
137
|
+
### AbortController / cancellation
|
|
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 (including `signal`):
|
|
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 })
|
|
122
148
|
|
|
123
|
-
|
|
149
|
+
// cancel the request
|
|
150
|
+
controller.abort()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Error handling
|
|
154
|
+
|
|
155
|
+
Non-2xx responses throw — the caught value is the parsed response body:
|
|
124
156
|
|
|
125
157
|
```ts
|
|
126
158
|
try {
|
|
127
|
-
|
|
159
|
+
await api.pet.getPetById(404)
|
|
128
160
|
} catch (e) {
|
|
129
|
-
console.log(e) //
|
|
161
|
+
console.log(e) // awaited response.json()
|
|
130
162
|
}
|
|
131
163
|
```
|
|
132
164
|
|
|
133
|
-
|
|
165
|
+
Override `ParseError` to control the shape:
|
|
134
166
|
|
|
135
167
|
```ts
|
|
136
168
|
class MyClient extends ApiClient {
|
|
137
169
|
async ParseError(rep: Response) {
|
|
138
|
-
// do what you want
|
|
139
170
|
return { code: "API_ERROR" }
|
|
140
171
|
}
|
|
141
172
|
}
|
|
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
173
|
```
|
|
150
174
|
|
|
151
|
-
### Base
|
|
175
|
+
### Base URL resolving
|
|
152
176
|
|
|
153
|
-
|
|
177
|
+
By default uses the [URL constructor](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL): `new URL(path, baseUrl)`. Notable behavior:
|
|
154
178
|
|
|
155
|
-
- `new URL("/v2/cats", "https://example.com/v1/")
|
|
156
|
-
- `new URL("v2/cats", "https://example.com/v1/")
|
|
179
|
+
- `new URL("/v2/cats", "https://example.com/v1/")` → `https://example.com/v2/cats`
|
|
180
|
+
- `new URL("v2/cats", "https://example.com/v1/")` → `https://example.com/v1/v2/cats`
|
|
157
181
|
|
|
158
|
-
|
|
182
|
+
Override `PrepareFetchUrl` to change this (see [#2](https://github.com/vladkens/apigen-ts/issues/2)):
|
|
159
183
|
|
|
160
184
|
```ts
|
|
161
185
|
class MyClient extends ApiClient {
|
|
@@ -165,42 +189,39 @@ class MyClient extends ApiClient {
|
|
|
165
189
|
}
|
|
166
190
|
|
|
167
191
|
const api = new MyClient({ baseUrl: "https://example.com/v1" })
|
|
168
|
-
|
|
169
|
-
const pet = await api.pet.getPetById(404)
|
|
192
|
+
await api.pet.getPetById(1) // → https://example.com/v1/pet/1
|
|
170
193
|
```
|
|
171
194
|
|
|
172
195
|
### Node.js API
|
|
173
196
|
|
|
174
|
-
Create file like `apigen.mjs` with content:
|
|
175
|
-
|
|
176
197
|
```js
|
|
177
198
|
import { apigen } from "apigen-ts"
|
|
178
199
|
|
|
179
200
|
await apigen({
|
|
180
201
|
source: "https://petstore3.swagger.io/api/v3/openapi.json",
|
|
181
202
|
output: "./api-client.ts",
|
|
182
|
-
//
|
|
183
|
-
name: "MyApiClient", // default "ApiClient"
|
|
184
|
-
parseDates: true, // default false
|
|
185
|
-
inlineEnums: false, // default false
|
|
186
|
-
|
|
203
|
+
// optional:
|
|
204
|
+
name: "MyApiClient", // default: "ApiClient"
|
|
205
|
+
parseDates: true, // default: false
|
|
206
|
+
inlineEnums: false, // default: false
|
|
207
|
+
fetchOptions: true, // default: false
|
|
208
|
+
filterPaths: /^\/pets/, // only include paths matching regex
|
|
209
|
+
includeTags: ["pets", "store"], // only include these tags
|
|
210
|
+
excludeTags: ["internal"], // exclude these tags (wins over includeTags)
|
|
211
|
+
headers: { "x-api-key": "secret-key" },
|
|
187
212
|
resolveName(ctx, op, proposal) {
|
|
188
|
-
// proposal is [
|
|
189
|
-
if (proposal[0] === "users") return //
|
|
213
|
+
// proposal is [namespace, methodName]
|
|
214
|
+
if (proposal[0] === "users") return // use default
|
|
190
215
|
|
|
191
|
-
const [a, b] = op.name.split("/").slice(3, 5) //
|
|
192
|
-
return [a, `${op.method}_${b}`] //
|
|
216
|
+
const [a, b] = op.name.split("/").slice(3, 5) // /api/v1/store/items/search
|
|
217
|
+
return [a, `${op.method}_${b}`] // → api.store.get_items()
|
|
193
218
|
},
|
|
194
219
|
})
|
|
195
220
|
```
|
|
196
221
|
|
|
197
|
-
|
|
222
|
+
## Usage with FastAPI
|
|
198
223
|
|
|
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.
|
|
224
|
+
By default, FastAPI generates verbose `operationId`s. Fix with a custom resolver:
|
|
204
225
|
|
|
205
226
|
```py
|
|
206
227
|
from fastapi import FastAPI
|
|
@@ -216,19 +237,18 @@ def update_operation_ids(app: FastAPI) -> None:
|
|
|
216
237
|
ns = route.tags[0] if route.tags else "general"
|
|
217
238
|
route.operation_id = f"{ns}_{route.name}".lower()
|
|
218
239
|
|
|
219
|
-
|
|
220
|
-
# this function should be after all routes added
|
|
240
|
+
# call after all routes are added
|
|
221
241
|
update_operation_ids(app)
|
|
222
242
|
```
|
|
223
243
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
## Compare
|
|
244
|
+
## Alternatives
|
|
227
245
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
246
|
+
| Package | Issue |
|
|
247
|
+
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
248
|
+
| [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)) |
|
|
249
|
+
| [openapi-typescript](https://github.com/drwpow/openapi-typescript) | Low-level types only — no callable client, no named type exports |
|
|
250
|
+
| [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli) | Wraps a Java library |
|
|
251
|
+
| [swagger-typescript-api](https://github.com/acacode/swagger-typescript-api) | Complex config, breaking API changes between versions |
|
|
232
252
|
|
|
233
253
|
## Development
|
|
234
254
|
|
package/dist/cli.cjs
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var main$1 = require('./main-C0qK6dZX.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-l0LIDQ3K.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();
|