typedapi-client-helpers 0.1.3 → 0.1.5
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 +597 -88
- package/dist/index.cjs +590 -130
- package/dist/index.d.cts +74 -98
- package/dist/index.d.ts +74 -98
- package/dist/index.js +588 -125
- package/package.json +32 -39
- package/scripts/generate-api-minimal.mjs +740 -735
package/README.md
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
# typedapi-client-helpers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## What this package includes
|
|
6
|
-
|
|
7
|
-
* `typedapi-generate` CLI command
|
|
8
|
-
* OpenAPI TypeScript client generation
|
|
9
|
-
* Typed API wrapper methods
|
|
10
|
-
* Typed query and pagination helpers
|
|
11
|
-
* `ApiResult<T>` response wrapper
|
|
12
|
-
* Toast helpers using `react-toastify`
|
|
13
|
-
* `TypedApiToastProvider`
|
|
14
|
-
* `buildQuery` helper for filters, paging, and sorting
|
|
15
|
-
* `toFormData` helper for file uploads
|
|
16
|
-
* Unauthorized handling for `401` responses
|
|
17
|
-
* Sort direction conversion helpers
|
|
3
|
+
Generate a typed TypeScript API client from an OpenAPI/Swagger document and use reusable helpers for API responses, filtering, sorting, pagination, callbacks, and multipart form data.
|
|
18
4
|
|
|
19
|
-
|
|
5
|
+
The generated client uses the native `fetch` API and is created inside your own project, so the generated models and methods remain available for inspection and customization.
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
* Generates TypeScript models and API methods from OpenAPI
|
|
10
|
+
* Uses the native `fetch` API
|
|
11
|
+
* Generates methods grouped by OpenAPI tags
|
|
12
|
+
* Creates typed wrapper methods around generated requests
|
|
13
|
+
* Returns a consistent `ApiResult<T>`
|
|
14
|
+
* Supports success and error callbacks
|
|
15
|
+
* Supports custom request parameters and headers
|
|
16
|
+
* Supports query parameters, pagination, filtering, and sorting
|
|
17
|
+
* Converts upload payloads to `FormData`
|
|
18
|
+
* Supports local Swagger files and remote Swagger URLs
|
|
19
|
+
* Supports type-only imports
|
|
20
|
+
* Works with TypeScript frontend projects such as React, Angular, and Vue
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
## Installation
|
|
26
23
|
|
|
27
24
|
```bash
|
|
28
|
-
npm install
|
|
25
|
+
npm install typedapi-client-helpers
|
|
29
26
|
```
|
|
30
27
|
|
|
31
|
-
##
|
|
28
|
+
## Add the generator command
|
|
32
29
|
|
|
33
|
-
Add
|
|
30
|
+
Add the following script to your project's `package.json`:
|
|
34
31
|
|
|
35
32
|
```json
|
|
36
33
|
{
|
|
@@ -40,33 +37,41 @@ Add a script to your project:
|
|
|
40
37
|
}
|
|
41
38
|
```
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
You can then generate the API client with:
|
|
44
41
|
|
|
45
42
|
```bash
|
|
46
43
|
npm run generate:api
|
|
47
44
|
```
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
## Configuration
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
https://localhost:7000/swagger/v1/swagger.json
|
|
53
|
-
```
|
|
48
|
+
The generator can be configured through the `config` property in your project's `package.json`.
|
|
54
49
|
|
|
55
|
-
|
|
50
|
+
### Generate from a Swagger URL
|
|
56
51
|
|
|
57
|
-
```
|
|
58
|
-
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"scripts": {
|
|
55
|
+
"generate:api": "typedapi-generate"
|
|
56
|
+
},
|
|
57
|
+
"config": {
|
|
58
|
+
"swaggerUrl": "https://localhost:7000/swagger/v1/swagger.json",
|
|
59
|
+
"apiOutput": "src/api",
|
|
60
|
+
"typedApiUseTypeOnlyImports": true,
|
|
61
|
+
"typedApiUseFilterFormValues": false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
59
64
|
```
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Configure the generator in your `package.json`:
|
|
66
|
+
### Generate from a local Swagger file
|
|
64
67
|
|
|
65
68
|
```json
|
|
66
69
|
{
|
|
70
|
+
"scripts": {
|
|
71
|
+
"generate:api": "typedapi-generate"
|
|
72
|
+
},
|
|
67
73
|
"config": {
|
|
68
|
-
"
|
|
69
|
-
"swaggerFile": "./swagger.json",
|
|
74
|
+
"swaggerFile": "swagger/openapi.json",
|
|
70
75
|
"apiOutput": "src/api",
|
|
71
76
|
"typedApiUseTypeOnlyImports": true,
|
|
72
77
|
"typedApiUseFilterFormValues": false
|
|
@@ -74,94 +79,598 @@ Configure the generator in your `package.json`:
|
|
|
74
79
|
}
|
|
75
80
|
```
|
|
76
81
|
|
|
77
|
-
|
|
82
|
+
When `swaggerFile` is configured, it takes precedence over `swaggerUrl`.
|
|
83
|
+
|
|
84
|
+
## Configuration options
|
|
85
|
+
|
|
86
|
+
| Option | Default | Description |
|
|
87
|
+
| ----------------------------- | ------------------------------------------------ | -------------------------------------------------------- |
|
|
88
|
+
| `swaggerUrl` | `https://localhost:7000/swagger/v1/swagger.json` | URL of the OpenAPI document |
|
|
89
|
+
| `swaggerFile` | — | Path to a local OpenAPI document |
|
|
90
|
+
| `apiOutput` | `src/api` | Directory where the generated API is written |
|
|
91
|
+
| `typedApiUseTypeOnlyImports` | `false` | Generates `import type` statements for type-only imports |
|
|
92
|
+
| `typedApiUseFilterFormValues` | `false` | Generates filter-form arguments for paginated methods |
|
|
93
|
+
|
|
94
|
+
## Environment variables
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
| ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------------ |
|
|
81
|
-
| `swaggerUrl` | URL to the Swagger/OpenAPI document | `https://localhost:7000/swagger/v1/swagger.json` |
|
|
82
|
-
| `swaggerFile` | Local Swagger/OpenAPI file. Overrides `swaggerUrl` when provided | Not set |
|
|
83
|
-
| `apiOutput` | Output directory for generated files | `src/api` |
|
|
84
|
-
| `typedApiUseTypeOnlyImports` | Uses `import type` statements when possible | `false` |
|
|
85
|
-
| `typedApiUseFilterFormValues` | Generates pagination methods using `FilterFormValues[]` | `false` |
|
|
96
|
+
Configuration can also be supplied through environment variables.
|
|
86
97
|
|
|
87
|
-
|
|
98
|
+
| Environment variable | Description |
|
|
99
|
+
| ---------------------------------- | -------------------------------------------------------- |
|
|
100
|
+
| `SWAGGER_URL` | URL of the OpenAPI document |
|
|
101
|
+
| `SWAGGER_FILE` | Path to a local OpenAPI document |
|
|
102
|
+
| `API_OUTPUT` | Generated API output directory |
|
|
103
|
+
| `TYPED_API_USE_TYPE_ONLY_IMPORTS` | Enables type-only imports when set to `true` |
|
|
104
|
+
| `TYPED_API_USE_FILTER_FORM_VALUES` | Enables filter-form method generation when set to `true` |
|
|
105
|
+
|
|
106
|
+
Example:
|
|
88
107
|
|
|
89
108
|
```bash
|
|
90
109
|
SWAGGER_URL=https://localhost:7000/swagger/v1/swagger.json npm run generate:api
|
|
91
|
-
SWAGGER_FILE=./swagger.json npm run generate:api
|
|
92
|
-
API_OUTPUT=src/api npm run generate:api
|
|
93
|
-
TYPED_API_USE_TYPE_ONLY_IMPORTS=true npm run generate:api
|
|
94
|
-
TYPED_API_USE_FILTER_FORM_VALUES=true npm run generate:api
|
|
95
110
|
```
|
|
96
111
|
|
|
97
|
-
|
|
112
|
+
Command-line npm configuration is also supported:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npm run generate:api --swagger-url=https://localhost:7000/swagger/v1/swagger.json
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm run generate:api --swagger-file=swagger/openapi.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm run generate:api --api-output=src/api
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Generated structure
|
|
127
|
+
|
|
128
|
+
Running the generator creates an API directory similar to:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
src/
|
|
132
|
+
└── api/
|
|
133
|
+
├── generated/
|
|
134
|
+
│ ├── data-contracts.ts
|
|
135
|
+
│ ├── http-client.ts
|
|
136
|
+
│ ├── Product.ts
|
|
137
|
+
│ ├── ProductRoute.ts
|
|
138
|
+
│ ├── Supplier.ts
|
|
139
|
+
│ └── SupplierRoute.ts
|
|
140
|
+
├── methods/
|
|
141
|
+
│ ├── Product.api.ts
|
|
142
|
+
│ └── Supplier.api.ts
|
|
143
|
+
└── index.ts
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The `generated` directory contains the client produced from the OpenAPI document.
|
|
147
|
+
|
|
148
|
+
The `methods` directory contains typed wrapper functions that:
|
|
98
149
|
|
|
99
|
-
|
|
150
|
+
* Handle successful and failed responses
|
|
151
|
+
* Return `ApiResult<T>`
|
|
152
|
+
* Support success and error callbacks
|
|
153
|
+
* Accept custom request parameters
|
|
154
|
+
* Convert multipart request objects to `FormData`
|
|
155
|
+
|
|
156
|
+
## Export the generated API
|
|
157
|
+
|
|
158
|
+
Create or update `src/api/index.ts` so consumers can import the generated types and methods from one location:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
export * from "./generated/data-contracts";
|
|
162
|
+
export * from "./generated/http-client";
|
|
163
|
+
|
|
164
|
+
export * from "./methods/Product.api";
|
|
165
|
+
export * from "./methods/Supplier.api";
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The exact method files depend on the tags in your OpenAPI document.
|
|
169
|
+
|
|
170
|
+
## Calling generated methods
|
|
171
|
+
|
|
172
|
+
Generated wrapper methods return a typed `ApiResult<T>`.
|
|
100
173
|
|
|
101
174
|
```ts
|
|
102
|
-
import {
|
|
175
|
+
import { getSuppliers } from "./api";
|
|
103
176
|
|
|
104
|
-
const result = await
|
|
177
|
+
const result = await getSuppliers({
|
|
178
|
+
pageNumber: 1,
|
|
179
|
+
pageSize: 25,
|
|
180
|
+
});
|
|
105
181
|
|
|
106
182
|
if (result.ok) {
|
|
107
|
-
|
|
183
|
+
console.log(result.response);
|
|
184
|
+
} else {
|
|
185
|
+
console.error(result.status, result.error);
|
|
108
186
|
}
|
|
109
187
|
```
|
|
110
188
|
|
|
111
|
-
|
|
189
|
+
`ApiResult<T>` is a discriminated union:
|
|
112
190
|
|
|
113
191
|
```ts
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
toastError: {
|
|
120
|
-
title: "Error",
|
|
121
|
-
message: "Unable to create product."
|
|
192
|
+
type ApiResult<T> =
|
|
193
|
+
| {
|
|
194
|
+
ok: true;
|
|
195
|
+
status: number;
|
|
196
|
+
response: T;
|
|
122
197
|
}
|
|
198
|
+
| {
|
|
199
|
+
ok: false;
|
|
200
|
+
status: number;
|
|
201
|
+
response?: T;
|
|
202
|
+
error: unknown;
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Checking `result.ok` automatically narrows the result to either the successful or failed response type.
|
|
207
|
+
|
|
208
|
+
## Success and error callbacks
|
|
209
|
+
|
|
210
|
+
Generated methods support optional success and error callbacks.
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { getSuppliers } from "./api";
|
|
214
|
+
|
|
215
|
+
const result = await getSuppliers(
|
|
216
|
+
{
|
|
217
|
+
pageNumber: 1,
|
|
218
|
+
pageSize: 25,
|
|
219
|
+
},
|
|
220
|
+
async (response) => {
|
|
221
|
+
console.log("Suppliers loaded:", response);
|
|
222
|
+
},
|
|
223
|
+
async (error) => {
|
|
224
|
+
console.error("Unable to load suppliers:", error);
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The method still returns an `ApiResult<T>`, even when callbacks are provided.
|
|
230
|
+
|
|
231
|
+
## Create and update requests
|
|
232
|
+
|
|
233
|
+
Body-based API methods keep the argument types generated from the OpenAPI document.
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
import { createSupplier } from "./api";
|
|
237
|
+
|
|
238
|
+
const result = await createSupplier({
|
|
239
|
+
id: "supplier-1",
|
|
240
|
+
name: "Example Supplier",
|
|
241
|
+
active: true,
|
|
242
|
+
countryCode: "NL",
|
|
243
|
+
email: "supplier@example.com",
|
|
244
|
+
rating: 4.5,
|
|
123
245
|
});
|
|
246
|
+
|
|
247
|
+
if (!result.ok) {
|
|
248
|
+
console.error("Supplier creation failed:", result.error);
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Callbacks can be added after the generated API arguments:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
await createSupplier(
|
|
256
|
+
{
|
|
257
|
+
id: "supplier-1",
|
|
258
|
+
name: "Example Supplier",
|
|
259
|
+
active: true,
|
|
260
|
+
countryCode: "NL",
|
|
261
|
+
email: "supplier@example.com",
|
|
262
|
+
rating: 4.5,
|
|
263
|
+
},
|
|
264
|
+
(supplier) => {
|
|
265
|
+
console.log("Created supplier:", supplier);
|
|
266
|
+
},
|
|
267
|
+
(error) => {
|
|
268
|
+
console.error("Creation failed:", error);
|
|
269
|
+
},
|
|
270
|
+
);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Custom request parameters
|
|
274
|
+
|
|
275
|
+
The final argument of a generated wrapper method can contain request configuration supported by the generated HTTP client.
|
|
276
|
+
|
|
277
|
+
For example, custom headers can be supplied as follows:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { getSuppliers } from "./api";
|
|
281
|
+
|
|
282
|
+
const result = await getSuppliers(
|
|
283
|
+
{
|
|
284
|
+
pageNumber: 1,
|
|
285
|
+
pageSize: 25,
|
|
286
|
+
},
|
|
287
|
+
undefined,
|
|
288
|
+
undefined,
|
|
289
|
+
{
|
|
290
|
+
headers: {
|
|
291
|
+
Authorization: `Bearer ${token}`,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
);
|
|
124
295
|
```
|
|
125
296
|
|
|
126
|
-
|
|
297
|
+
## Filter-form generation
|
|
127
298
|
|
|
128
|
-
|
|
129
|
-
import { TypedApiToastProvider } from "typedapi-client-helpers";
|
|
299
|
+
Set `typedApiUseFilterFormValues` to `true` to generate paginated methods that accept filter-form values rather than a query object.
|
|
130
300
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"config": {
|
|
304
|
+
"swaggerUrl": "https://localhost:7000/swagger/v1/swagger.json",
|
|
305
|
+
"apiOutput": "src/api",
|
|
306
|
+
"typedApiUseFilterFormValues": true
|
|
307
|
+
}
|
|
138
308
|
}
|
|
139
309
|
```
|
|
140
310
|
|
|
141
|
-
|
|
311
|
+
A generated paginated method can then be called with filters, pagination, and sorting:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import type { FilterFormValues } from "typedapi-client-helpers";
|
|
315
|
+
import {
|
|
316
|
+
getSuppliers,
|
|
317
|
+
type GetSuppliersQuery,
|
|
318
|
+
} from "./api";
|
|
319
|
+
|
|
320
|
+
const filters: FilterFormValues<GetSuppliersQuery>[] = [
|
|
321
|
+
{
|
|
322
|
+
name: "Supplier name",
|
|
323
|
+
filterName: "name",
|
|
324
|
+
type: "string",
|
|
325
|
+
value: "Example",
|
|
326
|
+
isAList: false,
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "Active",
|
|
330
|
+
filterName: "active",
|
|
331
|
+
type: "boolean",
|
|
332
|
+
value: true,
|
|
333
|
+
isAList: false,
|
|
334
|
+
},
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
const result = await getSuppliers(
|
|
338
|
+
filters,
|
|
339
|
+
1,
|
|
340
|
+
25,
|
|
341
|
+
"name",
|
|
342
|
+
"Ascending",
|
|
343
|
+
);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Empty filter values are ignored automatically.
|
|
347
|
+
|
|
348
|
+
## Supported filter types
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
type FilterType =
|
|
352
|
+
| "number"
|
|
353
|
+
| "date"
|
|
354
|
+
| "string"
|
|
355
|
+
| "timespan"
|
|
356
|
+
| "boolean"
|
|
357
|
+
| "boolean-button"
|
|
358
|
+
| "OptionValue";
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
A filter configuration uses the following structure:
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
interface FilterFormValues<TQuery> {
|
|
365
|
+
name: string;
|
|
366
|
+
filterName: keyof TQuery;
|
|
367
|
+
filterNameMax?: keyof TQuery;
|
|
368
|
+
type: FilterType;
|
|
369
|
+
value:
|
|
370
|
+
| number
|
|
371
|
+
| string
|
|
372
|
+
| Date
|
|
373
|
+
| boolean
|
|
374
|
+
| null
|
|
375
|
+
| OptionValue[]
|
|
376
|
+
| string[];
|
|
377
|
+
maxValue?: number | string | Date | null;
|
|
378
|
+
isAList: boolean;
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Range filters
|
|
383
|
+
|
|
384
|
+
Use `filterNameMax` and `maxValue` to create range filters:
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
const filters: FilterFormValues<GetSuppliersQuery>[] = [
|
|
388
|
+
{
|
|
389
|
+
name: "Minimum rating",
|
|
390
|
+
filterName: "minimumRating",
|
|
391
|
+
filterNameMax: "maximumRating",
|
|
392
|
+
type: "number",
|
|
393
|
+
value: 3,
|
|
394
|
+
maxValue: 5,
|
|
395
|
+
isAList: false,
|
|
396
|
+
},
|
|
397
|
+
];
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Option filters
|
|
401
|
+
|
|
402
|
+
Option-based filters display a name while sending a separate value to the API.
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
import type {
|
|
406
|
+
FilterFormValues,
|
|
407
|
+
OptionValue,
|
|
408
|
+
} from "typedapi-client-helpers";
|
|
409
|
+
|
|
410
|
+
const countries: OptionValue[] = [
|
|
411
|
+
{
|
|
412
|
+
name: "Netherlands",
|
|
413
|
+
value: "NL",
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: "Belgium",
|
|
417
|
+
value: "BE",
|
|
418
|
+
},
|
|
419
|
+
];
|
|
420
|
+
|
|
421
|
+
const filters: FilterFormValues<GetSuppliersQuery>[] = [
|
|
422
|
+
{
|
|
423
|
+
name: "Countries",
|
|
424
|
+
filterName: "countryCodes",
|
|
425
|
+
type: "OptionValue",
|
|
426
|
+
value: countries,
|
|
427
|
+
isAList: true,
|
|
428
|
+
},
|
|
429
|
+
];
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The generator sends the values `"NL"` and `"BE"` rather than the display names.
|
|
433
|
+
|
|
434
|
+
## Using `buildQuery` directly
|
|
435
|
+
|
|
436
|
+
The `buildQuery` helper can also be used without generated filter-form methods.
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
import {
|
|
440
|
+
buildQuery,
|
|
441
|
+
type FilterFormValues,
|
|
442
|
+
} from "typedapi-client-helpers";
|
|
443
|
+
|
|
444
|
+
interface ProductQuery {
|
|
445
|
+
name?: string;
|
|
446
|
+
minimumPrice?: number;
|
|
447
|
+
maximumPrice?: number;
|
|
448
|
+
pageNumber?: number;
|
|
449
|
+
pageSize?: number;
|
|
450
|
+
sortBy?: string;
|
|
451
|
+
sortDirection?: string;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const filters: FilterFormValues<ProductQuery>[] = [
|
|
455
|
+
{
|
|
456
|
+
name: "Name",
|
|
457
|
+
filterName: "name",
|
|
458
|
+
type: "string",
|
|
459
|
+
value: "Laptop",
|
|
460
|
+
isAList: false,
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: "Price",
|
|
464
|
+
filterName: "minimumPrice",
|
|
465
|
+
filterNameMax: "maximumPrice",
|
|
466
|
+
type: "number",
|
|
467
|
+
value: 500,
|
|
468
|
+
maxValue: 1500,
|
|
469
|
+
isAList: false,
|
|
470
|
+
},
|
|
471
|
+
];
|
|
472
|
+
|
|
473
|
+
const query = buildQuery<ProductQuery>(
|
|
474
|
+
filters,
|
|
475
|
+
1,
|
|
476
|
+
25,
|
|
477
|
+
"name",
|
|
478
|
+
"Ascending",
|
|
479
|
+
);
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
The result is equivalent to:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
{
|
|
486
|
+
name: "Laptop",
|
|
487
|
+
minimumPrice: 500,
|
|
488
|
+
maximumPrice: 1500,
|
|
489
|
+
pageNumber: 1,
|
|
490
|
+
pageSize: 25,
|
|
491
|
+
sortBy: "name",
|
|
492
|
+
sortDirection: "Ascending"
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## Sorting helpers
|
|
497
|
+
|
|
498
|
+
The package includes helpers for converting between UI sort states and API sort directions.
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
import {
|
|
502
|
+
getSortDirectionFromSortType,
|
|
503
|
+
getSortTypeFromSortDirection,
|
|
504
|
+
} from "typedapi-client-helpers";
|
|
505
|
+
|
|
506
|
+
const apiDirection =
|
|
507
|
+
getSortDirectionFromSortType("Neutral");
|
|
508
|
+
|
|
509
|
+
// "Default"
|
|
510
|
+
|
|
511
|
+
const uiSortType =
|
|
512
|
+
getSortTypeFromSortDirection("Descending");
|
|
513
|
+
|
|
514
|
+
// "Descending"
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Supported UI sort states are:
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
type SortType =
|
|
521
|
+
| "Default"
|
|
522
|
+
| "Neutral"
|
|
523
|
+
| "Ascending"
|
|
524
|
+
| "Descending";
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
`Neutral` is a UI-only state and is converted to `Default` before it is sent to the API.
|
|
528
|
+
|
|
529
|
+
## File uploads
|
|
530
|
+
|
|
531
|
+
Multipart methods are detected during generation. Their payloads are converted to `FormData` automatically.
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
import { uploadDocument } from "./api";
|
|
535
|
+
|
|
536
|
+
const result = await uploadDocument({
|
|
537
|
+
file: selectedFile,
|
|
538
|
+
description: "Supplier contract",
|
|
539
|
+
});
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
The `toFormData` helper supports:
|
|
543
|
+
|
|
544
|
+
* `File`
|
|
545
|
+
* `Blob`
|
|
546
|
+
* Arrays of files or values
|
|
547
|
+
* Strings
|
|
548
|
+
* Numbers
|
|
549
|
+
* Booleans
|
|
550
|
+
* Nested objects
|
|
551
|
+
|
|
552
|
+
Nested objects are serialized as JSON strings.
|
|
553
|
+
|
|
554
|
+
You can also use the helper directly:
|
|
142
555
|
|
|
143
556
|
```ts
|
|
144
557
|
import { toFormData } from "typedapi-client-helpers";
|
|
145
558
|
|
|
146
559
|
const formData = toFormData({
|
|
147
|
-
|
|
148
|
-
|
|
560
|
+
file: selectedFile,
|
|
561
|
+
tags: ["contract", "supplier"],
|
|
562
|
+
metadata: {
|
|
563
|
+
department: "Purchasing",
|
|
564
|
+
},
|
|
149
565
|
});
|
|
150
566
|
```
|
|
151
567
|
|
|
152
|
-
##
|
|
568
|
+
## Handling HTTP responses directly
|
|
153
569
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
570
|
+
The `handleApiResponse` helper can wrap any compatible generated HTTP request:
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
import {
|
|
574
|
+
handleApiResponse,
|
|
575
|
+
type HttpResponse,
|
|
576
|
+
} from "typedapi-client-helpers";
|
|
577
|
+
|
|
578
|
+
interface Product {
|
|
579
|
+
id: string;
|
|
580
|
+
name: string;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async function requestProduct(): Promise<
|
|
584
|
+
HttpResponse<Product>
|
|
585
|
+
> {
|
|
586
|
+
// Return a compatible generated HTTP response.
|
|
587
|
+
throw new Error("Implement request");
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const result = await handleApiResponse(
|
|
591
|
+
() => requestProduct(),
|
|
592
|
+
{
|
|
593
|
+
onSuccess: (product) => {
|
|
594
|
+
console.log(product);
|
|
595
|
+
},
|
|
596
|
+
onError: (error) => {
|
|
597
|
+
console.error(error);
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
);
|
|
163
601
|
```
|
|
164
602
|
|
|
165
|
-
|
|
603
|
+
The response body is parsed as JSON when the response content type contains `application/json`. Empty responses, including HTTP `204` responses, return `undefined`.
|
|
604
|
+
|
|
605
|
+
## Regenerating the client
|
|
166
606
|
|
|
167
|
-
|
|
607
|
+
Run the generator whenever the OpenAPI document changes:
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
npm run generate:api
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
The existing generated and method directories inside the configured API output are replaced during generation. Keep custom application code outside those generated directories.
|
|
614
|
+
|
|
615
|
+
## Recommended workflow
|
|
616
|
+
|
|
617
|
+
1. Start the backend API.
|
|
618
|
+
2. Confirm that its OpenAPI document is available.
|
|
619
|
+
3. Run the API generator.
|
|
620
|
+
4. Build or start the frontend application.
|
|
621
|
+
|
|
622
|
+
Example:
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
npm run generate:api
|
|
626
|
+
npm run dev
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
## Package exports
|
|
630
|
+
|
|
631
|
+
The package exports the following reusable types and helpers:
|
|
632
|
+
|
|
633
|
+
```ts
|
|
634
|
+
import {
|
|
635
|
+
buildQuery,
|
|
636
|
+
getSortDirectionFromSortType,
|
|
637
|
+
getSortTypeFromSortDirection,
|
|
638
|
+
handleApiResponse,
|
|
639
|
+
sortTypes,
|
|
640
|
+
toFormData,
|
|
641
|
+
} from "typedapi-client-helpers";
|
|
642
|
+
|
|
643
|
+
import type {
|
|
644
|
+
ApiErrorHandler,
|
|
645
|
+
ApiMethodCallbacks,
|
|
646
|
+
ApiResult,
|
|
647
|
+
ApiSuccessHandler,
|
|
648
|
+
FilterFormValues,
|
|
649
|
+
FilterType,
|
|
650
|
+
HttpResponse,
|
|
651
|
+
OptionValue,
|
|
652
|
+
SortType,
|
|
653
|
+
} from "typedapi-client-helpers";
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Generated API models and methods are written into your project and should be imported from your configured API output directory:
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
import {
|
|
660
|
+
createSupplier,
|
|
661
|
+
getSuppliers,
|
|
662
|
+
type SupplierModel,
|
|
663
|
+
} from "./api";
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
## TypeScript
|
|
667
|
+
|
|
668
|
+
The package includes generated declaration files and can be used directly in TypeScript projects.
|
|
669
|
+
|
|
670
|
+
```ts
|
|
671
|
+
import type {
|
|
672
|
+
ApiResult,
|
|
673
|
+
FilterFormValues,
|
|
674
|
+
SortType,
|
|
675
|
+
} from "typedapi-client-helpers";
|
|
676
|
+
```
|