nuxt-openapi-hyperfetch 0.1.8-alpha.1 → 0.2.8-alpha.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/.editorconfig +26 -26
- package/.prettierignore +17 -17
- package/CONTRIBUTING.md +291 -291
- package/INSTRUCTIONS.md +327 -327
- package/LICENSE +202 -202
- package/README.md +231 -231
- package/dist/cli/config.d.ts +9 -2
- package/dist/cli/config.js +1 -1
- package/dist/cli/logo.js +5 -5
- package/dist/cli/messages.d.ts +1 -0
- package/dist/cli/messages.js +2 -0
- package/dist/cli/prompts.d.ts +5 -0
- package/dist/cli/prompts.js +12 -0
- package/dist/cli/types.d.ts +1 -1
- package/dist/generators/components/connector-generator/templates.js +12 -12
- package/dist/generators/use-async-data/templates.js +17 -17
- package/dist/generators/use-fetch/templates.js +14 -14
- package/dist/index.js +39 -27
- package/dist/module/index.js +19 -0
- package/dist/module/types.d.ts +7 -0
- package/docs/API-REFERENCE.md +886 -886
- package/docs/generated-components.md +615 -0
- package/docs/headless-composables-ui.md +569 -0
- package/eslint.config.js +85 -85
- package/package.json +8 -2
- package/src/cli/config.ts +147 -140
- package/src/cli/logger.ts +124 -124
- package/src/cli/logo.ts +25 -25
- package/src/cli/messages.ts +4 -0
- package/src/cli/prompts.ts +14 -1
- package/src/cli/types.ts +50 -50
- package/src/generators/components/connector-generator/generator.ts +138 -0
- package/src/generators/components/connector-generator/templates.ts +254 -0
- package/src/generators/components/connector-generator/types.ts +34 -0
- package/src/generators/components/schema-analyzer/index.ts +44 -0
- package/src/generators/components/schema-analyzer/intent-detector.ts +187 -0
- package/src/generators/components/schema-analyzer/openapi-reader.ts +96 -0
- package/src/generators/components/schema-analyzer/resource-grouper.ts +166 -0
- package/src/generators/components/schema-analyzer/schema-field-mapper.ts +268 -0
- package/src/generators/components/schema-analyzer/types.ts +177 -0
- package/src/generators/nuxt-server/generator.ts +272 -272
- package/src/generators/shared/runtime/apiHelpers.ts +535 -507
- package/src/generators/shared/runtime/pagination.ts +323 -0
- package/src/generators/shared/runtime/useDeleteConnector.ts +109 -0
- package/src/generators/shared/runtime/useDetailConnector.ts +64 -0
- package/src/generators/shared/runtime/useFormConnector.ts +139 -0
- package/src/generators/shared/runtime/useListConnector.ts +148 -0
- package/src/generators/shared/runtime/zod-error-merger.ts +119 -0
- package/src/generators/shared/templates/api-callbacks-plugin.ts +399 -352
- package/src/generators/shared/templates/api-pagination-plugin.ts +158 -0
- package/src/generators/use-async-data/generator.ts +205 -205
- package/src/generators/use-async-data/runtime/useApiAsyncData.ts +329 -229
- package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +324 -245
- package/src/generators/use-async-data/templates.ts +257 -257
- package/src/generators/use-fetch/generator.ts +170 -170
- package/src/generators/use-fetch/runtime/useApiRequest.ts +354 -234
- package/src/generators/use-fetch/templates.ts +214 -214
- package/src/index.ts +305 -265
- package/src/module/index.ts +158 -133
- package/src/module/types.ts +39 -31
package/docs/API-REFERENCE.md
CHANGED
|
@@ -1,886 +1,886 @@
|
|
|
1
|
-
# 📖 API Reference - Complete Technical Reference
|
|
2
|
-
|
|
3
|
-
> **Purpose**: Complete technical documentation of all interfaces, functions, and configuration options.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [CLI Commands](#cli-commands)
|
|
8
|
-
- [Configuration](#configuration)
|
|
9
|
-
- [Interfaces & Types](#interfaces--types)
|
|
10
|
-
- [Parser API](#parser-api)
|
|
11
|
-
- [Template API](#template-api)
|
|
12
|
-
- [Runtime APIs](#runtime-apis)
|
|
13
|
-
- [Callback Interfaces](#callback-interfaces)
|
|
14
|
-
- [Generated Composables](#generated-composables)
|
|
15
|
-
|
|
16
|
-
## CLI Commands
|
|
17
|
-
|
|
18
|
-
### `generate`
|
|
19
|
-
|
|
20
|
-
Generate Nuxt composables from OpenAPI/Swagger specifications.
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
nxh generate [options]
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**Options:**
|
|
27
|
-
|
|
28
|
-
| Option | Alias | Type | Description |
|
|
29
|
-
| ----------------------- | ----- | -------- | -------------------------- |
|
|
30
|
-
| `--input <file>` | `-i` | `string` | OpenAPI/Swagger file path |
|
|
31
|
-
| `--output <directory>` | `-o` | `string` | Output directory |
|
|
32
|
-
| `--composables <types>` | `-c` | `string` | Comma-separated types list |
|
|
33
|
-
|
|
34
|
-
**Composable Types:**
|
|
35
|
-
|
|
36
|
-
- `useFetch` - Nuxt 3 useFetch composables
|
|
37
|
-
- `useAsyncData` - Nuxt 3 useAsyncData composables (normal + raw)
|
|
38
|
-
- `nuxt-server` - Nuxt server routes with BFF pattern
|
|
39
|
-
|
|
40
|
-
**Interactive Mode:**
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
nxh generate
|
|
44
|
-
# Prompts for input file, output directory, and composable types
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**Non-Interactive Mode:**
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
nxh generate -i swagger.yaml -o ./swagger
|
|
51
|
-
# Still prompts for composable types
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**Fully Automated:**
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
echo "useFetch" | nxh generate -i swagger.yaml -o ./swagger
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**Exit Codes:**
|
|
61
|
-
|
|
62
|
-
- `0` - Success
|
|
63
|
-
- `1` - Error (missing files, parsing errors, etc.)
|
|
64
|
-
|
|
65
|
-
## Configuration
|
|
66
|
-
|
|
67
|
-
### OpenAPI Generator Config
|
|
68
|
-
|
|
69
|
-
Located in: `openapitools.json` (auto-created if missing)
|
|
70
|
-
|
|
71
|
-
```json
|
|
72
|
-
{
|
|
73
|
-
"generator-cli": {
|
|
74
|
-
"version": "7.14.0",
|
|
75
|
-
"generators": {
|
|
76
|
-
"typescript": {
|
|
77
|
-
"generatorName": "typescript-fetch",
|
|
78
|
-
"output": "./swagger"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Key Settings:**
|
|
86
|
-
|
|
87
|
-
- `generatorName`: Must be `typescript-fetch` (only supported target)
|
|
88
|
-
- `output`: Where OpenAPI Generator writes TypeScript files
|
|
89
|
-
- `version`: OpenAPI Generator CLI version
|
|
90
|
-
|
|
91
|
-
### Prettier Config
|
|
92
|
-
|
|
93
|
-
Generated files are formatted with Prettier using these settings:
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
{
|
|
97
|
-
parser: 'typescript',
|
|
98
|
-
singleQuote: true,
|
|
99
|
-
semi: true,
|
|
100
|
-
trailingComma: 'es5',
|
|
101
|
-
printWidth: 100
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## Interfaces & Types
|
|
106
|
-
|
|
107
|
-
### MethodInfo
|
|
108
|
-
|
|
109
|
-
**File**: `src/generators/shared/types.ts`
|
|
110
|
-
|
|
111
|
-
**Purpose**: Represents a single API endpoint with all information needed for code generation.
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
export interface MethodInfo {
|
|
115
|
-
// Basic info
|
|
116
|
-
name: string; // e.g., 'addPet'
|
|
117
|
-
composableName: string; // e.g., 'useFetchAddPet'
|
|
118
|
-
path: string; // e.g., '/pet' or '/pet/{petId}'
|
|
119
|
-
httpMethod: string; // 'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH'
|
|
120
|
-
|
|
121
|
-
// Type information
|
|
122
|
-
requestType?: string; // e.g., 'AddPetRequest'
|
|
123
|
-
responseType: string; // e.g., 'Pet'
|
|
124
|
-
|
|
125
|
-
// Request details
|
|
126
|
-
pathParams: string[]; // e.g., ['petId']
|
|
127
|
-
queryParams: string[]; // e.g., ['status', 'limit']
|
|
128
|
-
headers: Record<string, string>; // Default headers to include
|
|
129
|
-
hasBody: boolean; // true if method uses a request body
|
|
130
|
-
bodyField?: string; // e.g., 'pet' (from requestParameters['pet'])
|
|
131
|
-
hasQueryParams: boolean; // true if has query parameters
|
|
132
|
-
|
|
133
|
-
// Metadata
|
|
134
|
-
description?: string; // JSDoc description from the method
|
|
135
|
-
hasRawMethod: boolean; // true if xxxRaw() method exists
|
|
136
|
-
rawMethodName?: string; // e.g., 'addPetRaw'
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
**Example:**
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
{
|
|
144
|
-
name: 'addPet',
|
|
145
|
-
composableName: 'useFetchAddPet',
|
|
146
|
-
path: '/pet',
|
|
147
|
-
httpMethod: 'POST',
|
|
148
|
-
requestType: 'AddPetRequest',
|
|
149
|
-
responseType: 'Pet',
|
|
150
|
-
pathParams: [],
|
|
151
|
-
queryParams: [],
|
|
152
|
-
headers: {},
|
|
153
|
-
hasBody: true,
|
|
154
|
-
bodyField: 'pet',
|
|
155
|
-
hasQueryParams: false,
|
|
156
|
-
hasRawMethod: false
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### ApiClassInfo
|
|
161
|
-
|
|
162
|
-
**File**: `src/generators/shared/types.ts`
|
|
163
|
-
|
|
164
|
-
**Purpose**: Groups all methods from a single API class.
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
export interface ApiClassInfo {
|
|
168
|
-
className: string; // e.g., 'PetApi'
|
|
169
|
-
methods: MethodInfo[]; // All methods from this API class
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### RequestContext
|
|
174
|
-
|
|
175
|
-
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
176
|
-
|
|
177
|
-
**Purpose**: Context object passed to `onRequest` callback.
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
export interface RequestContext {
|
|
181
|
-
url: string; // Request URL
|
|
182
|
-
method: string; // HTTP method
|
|
183
|
-
body?: any; // Request body
|
|
184
|
-
headers?: Record<string, string>; // Headers
|
|
185
|
-
query?: Record<string, any>; // Query params
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
**Usage:**
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
onRequest: (ctx: RequestContext) => {
|
|
193
|
-
console.log(`Making ${ctx.method} request to ${ctx.url}`);
|
|
194
|
-
};
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### ModifiedRequestContext
|
|
198
|
-
|
|
199
|
-
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
200
|
-
|
|
201
|
-
**Purpose**: Returned by `onRequest` to modify the request.
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
export interface ModifiedRequestContext {
|
|
205
|
-
headers?: Record<string, string>; // Modified headers
|
|
206
|
-
query?: Record<string, any>; // Modified query params
|
|
207
|
-
body?: any; // Modified body
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
**Usage:**
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
onRequest: (ctx) => {
|
|
215
|
-
return {
|
|
216
|
-
headers: {
|
|
217
|
-
...ctx.headers,
|
|
218
|
-
Authorization: `Bearer ${token}`,
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
};
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### SuccessContext
|
|
225
|
-
|
|
226
|
-
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
227
|
-
|
|
228
|
-
**Purpose**: Context passed to `onSuccess` callback. Different for normal vs raw.
|
|
229
|
-
|
|
230
|
-
**Normal Version:**
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
export interface SuccessContext {
|
|
234
|
-
url: string;
|
|
235
|
-
method: string;
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
**Raw Version:**
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
export interface SuccessContext {
|
|
243
|
-
url: string;
|
|
244
|
-
method: string;
|
|
245
|
-
headers: Headers; // Response headers
|
|
246
|
-
status: number; // HTTP status code
|
|
247
|
-
statusText: string; // Status text
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
**Usage:**
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
// Normal
|
|
255
|
-
onSuccess: (data: Pet, ctx: SuccessContext) => {
|
|
256
|
-
console.log(`Got pet from ${ctx.url}`);
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// Raw
|
|
260
|
-
onSuccess: (data: Pet, ctx: SuccessContext) => {
|
|
261
|
-
const token = ctx.headers.get('X-Auth-Token');
|
|
262
|
-
console.log(`Status: ${ctx.status}`);
|
|
263
|
-
};
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### ErrorContext
|
|
267
|
-
|
|
268
|
-
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
269
|
-
|
|
270
|
-
**Purpose**: Context passed to `onError` callback.
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
export interface ErrorContext {
|
|
274
|
-
url: string;
|
|
275
|
-
method: string;
|
|
276
|
-
status?: number; // HTTP status if available
|
|
277
|
-
message: string; // Error message
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### FinishContext
|
|
282
|
-
|
|
283
|
-
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
284
|
-
|
|
285
|
-
**Purpose**: Context passed to `onFinish` callback.
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
export interface FinishContext {
|
|
289
|
-
success: boolean; // true if succeeded, false if error
|
|
290
|
-
data?: any; // Data if success
|
|
291
|
-
error?: any; // Error if failed
|
|
292
|
-
duration?: number; // Optional: request duration in ms
|
|
293
|
-
url: string;
|
|
294
|
-
method: string;
|
|
295
|
-
}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
### ApiRequestOptions
|
|
299
|
-
|
|
300
|
-
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
301
|
-
|
|
302
|
-
**Purpose**: Options for all `useApiRequest` calls.
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
export interface ApiRequestOptions<T> {
|
|
306
|
-
// useFetch native options
|
|
307
|
-
method?: string;
|
|
308
|
-
body?: any;
|
|
309
|
-
headers?: Record<string, string>;
|
|
310
|
-
query?: Record<string, any>;
|
|
311
|
-
baseURL?: string;
|
|
312
|
-
server?: boolean;
|
|
313
|
-
lazy?: boolean;
|
|
314
|
-
immediate?: boolean;
|
|
315
|
-
|
|
316
|
-
// Custom features
|
|
317
|
-
pick?: string[]; // Dot notation field selection
|
|
318
|
-
transform?: (data: T) => any; // Transform response
|
|
319
|
-
|
|
320
|
-
// Lifecycle callbacks
|
|
321
|
-
onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
|
|
322
|
-
onSuccess?: (data: T, ctx: SuccessContext) => void | false;
|
|
323
|
-
onError?: (error: any, ctx: ErrorContext) => void | false;
|
|
324
|
-
onFinish?: (ctx: FinishContext) => void;
|
|
325
|
-
|
|
326
|
-
// Global callbacks control
|
|
327
|
-
skipGlobalCallbacks?: boolean | Array<'onRequest' | 'onSuccess' | 'onError' | 'onFinish'>;
|
|
328
|
-
|
|
329
|
-
// Any other useFetch options
|
|
330
|
-
[key: string]: any;
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### ApiAsyncDataOptions
|
|
335
|
-
|
|
336
|
-
**File**: `src/generators/use-async-data/runtime/useApiAsyncData.ts`
|
|
337
|
-
|
|
338
|
-
**Purpose**: Options for `useAsyncData` composables (normal version).
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
export interface ApiAsyncDataOptions<T> {
|
|
342
|
-
// useAsyncData native options
|
|
343
|
-
server?: boolean;
|
|
344
|
-
lazy?: boolean;
|
|
345
|
-
immediate?: boolean;
|
|
346
|
-
dedupe?: 'cancel' | 'defer';
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Disable automatic refresh when reactive params change.
|
|
350
|
-
* Set to false to prevent re-fetching when params/url refs update.
|
|
351
|
-
* @default true
|
|
352
|
-
*/
|
|
353
|
-
watch?: boolean;
|
|
354
|
-
|
|
355
|
-
// Custom features
|
|
356
|
-
pick?: string[];
|
|
357
|
-
transform?: (data: T) => any;
|
|
358
|
-
|
|
359
|
-
// Lifecycle callbacks
|
|
360
|
-
onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
|
|
361
|
-
onSuccess?: (data: T) => void | false;
|
|
362
|
-
onError?: (error: any) => void | false;
|
|
363
|
-
onFinish?: (ctx: FinishContext) => void;
|
|
364
|
-
|
|
365
|
-
// Global callbacks control
|
|
366
|
-
skipGlobalCallbacks?: boolean | string[];
|
|
367
|
-
}
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### ApiAsyncDataRawOptions
|
|
371
|
-
|
|
372
|
-
**File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
|
|
373
|
-
|
|
374
|
-
**Purpose**: Options for `useAsyncData` raw composables.
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
export interface ApiAsyncDataRawOptions<T> extends ApiAsyncDataOptions<T> {
|
|
378
|
-
// Same as ApiAsyncDataOptions
|
|
379
|
-
// But onSuccess signature is different:
|
|
380
|
-
// onSuccess?: (data: T, ctx: SuccessContext) => void | false
|
|
381
|
-
// where ctx includes: headers, status, statusText
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
### RawResponse
|
|
386
|
-
|
|
387
|
-
**File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
|
|
388
|
-
|
|
389
|
-
**Purpose**: Response structure for raw composables.
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
export interface RawResponse<T> {
|
|
393
|
-
data: T; // Actual response data
|
|
394
|
-
headers: Headers; // Response headers
|
|
395
|
-
status: number; // HTTP status code
|
|
396
|
-
statusText: string; // Status text
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
**Usage:**
|
|
401
|
-
|
|
402
|
-
```typescript
|
|
403
|
-
const { data, pending } = useAsyncDataGetPetRaw({ petId: 123 });
|
|
404
|
-
// data.value = { data: Pet, headers: Headers, status: 200, statusText: 'OK' }
|
|
405
|
-
|
|
406
|
-
const pet = data.value?.data;
|
|
407
|
-
const authToken = data.value?.headers.get('X-Auth-Token');
|
|
408
|
-
const wasCreated = data.value?.status === 201;
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
## Parser API
|
|
412
|
-
|
|
413
|
-
### extractMethodInfo()
|
|
414
|
-
|
|
415
|
-
**File**: `src/generators/use-fetch/parser.ts`
|
|
416
|
-
|
|
417
|
-
**Signature:**
|
|
418
|
-
|
|
419
|
-
```typescript
|
|
420
|
-
function extractMethodInfo(method: MethodDeclaration, sourceFile: SourceFile): MethodInfo | null;
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
**Purpose**: Extract API information from a TypeScript method.
|
|
424
|
-
|
|
425
|
-
**Parameters:**
|
|
426
|
-
|
|
427
|
-
- `method`: ts-morph MethodDeclaration node
|
|
428
|
-
- `sourceFile`: ts-morph SourceFile for context
|
|
429
|
-
|
|
430
|
-
**Returns**: `MethodInfo` object or `null` if extraction fails
|
|
431
|
-
|
|
432
|
-
**Algorithm:**
|
|
433
|
-
|
|
434
|
-
1. Extract method name
|
|
435
|
-
2. Find `xxxRequestOpts()` method (contains HTTP details)
|
|
436
|
-
3. Parse return statement to get path, method, body, headers
|
|
437
|
-
4. Extract parameter types
|
|
438
|
-
5. Extract return type (response type)
|
|
439
|
-
6. Generate composable name and file name
|
|
440
|
-
|
|
441
|
-
**Error Handling**: Returns `null` on errors (logged to console)
|
|
442
|
-
|
|
443
|
-
### getApiFiles()
|
|
444
|
-
|
|
445
|
-
**File**: `src/generators/use-fetch/parser.ts`
|
|
446
|
-
|
|
447
|
-
**Signature:**
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
function getApiFiles(inputDir: string): string[];
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
**Purpose**: Find all API files in the `apis/` subdirectory.
|
|
454
|
-
|
|
455
|
-
**Parameters:**
|
|
456
|
-
|
|
457
|
-
- `inputDir`: Path to the root output directory (e.g., `./swagger`). The function appends `/apis` internally.
|
|
458
|
-
|
|
459
|
-
**Returns**: Array of absolute file paths
|
|
460
|
-
|
|
461
|
-
**Filters**: Only includes `*.ts` files that are not `index.ts`
|
|
462
|
-
|
|
463
|
-
### parseApiFile()
|
|
464
|
-
|
|
465
|
-
**File**: `src/generators/use-fetch/parser.ts`
|
|
466
|
-
|
|
467
|
-
**Signature:**
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
function parseApiFile(filePath: string): ApiClassInfo;
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
**Purpose**: Parse a single API file and extract all methods.
|
|
474
|
-
|
|
475
|
-
**Parameters:**
|
|
476
|
-
|
|
477
|
-
- `filePath`: Absolute path to API file
|
|
478
|
-
|
|
479
|
-
**Returns**: `ApiClassInfo` for the first class found in the file
|
|
480
|
-
|
|
481
|
-
**Algorithm:**
|
|
482
|
-
|
|
483
|
-
1. Create ts-morph Project
|
|
484
|
-
2. Add source file
|
|
485
|
-
3. Find all classes
|
|
486
|
-
4. For each class:
|
|
487
|
-
- Extract all methods
|
|
488
|
-
- Call `extractMethodInfo()` for each
|
|
489
|
-
- Filter out nulls
|
|
490
|
-
5. Return array of ApiClassInfo
|
|
491
|
-
|
|
492
|
-
## Template API
|
|
493
|
-
|
|
494
|
-
### generateFileHeader()
|
|
495
|
-
|
|
496
|
-
**File**: `src/generators/use-fetch/templates.ts`
|
|
497
|
-
|
|
498
|
-
**Signature:**
|
|
499
|
-
|
|
500
|
-
```typescript
|
|
501
|
-
function generateFileHeader(): string;
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
**Returns**: TypeScript header comment
|
|
505
|
-
|
|
506
|
-
```typescript
|
|
507
|
-
// @ts-nocheck - This file runs in user's Nuxt project with different tsconfig
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
### generateImports()
|
|
511
|
-
|
|
512
|
-
**File**: `src/generators/use-fetch/templates.ts`
|
|
513
|
-
|
|
514
|
-
**Signature:**
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
function generateImports(method: MethodInfo, apiImportPath: string): string;
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
**Purpose**: Generate import statements for composable file.
|
|
521
|
-
|
|
522
|
-
**Returns:**
|
|
523
|
-
|
|
524
|
-
```typescript
|
|
525
|
-
import type { AddPetRequest, Pet } from '../apis/PetApi';
|
|
526
|
-
import { useApiRequest, type ApiRequestOptions } from '../runtime/useApiRequest';
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### generateFunctionBody()
|
|
530
|
-
|
|
531
|
-
**File**: `src/generators/use-fetch/templates.ts`
|
|
532
|
-
|
|
533
|
-
**Signature:**
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
function generateFunctionBody(method: MethodInfo): string;
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
**Purpose**: Generate composable function code.
|
|
540
|
-
|
|
541
|
-
**Returns:**
|
|
542
|
-
|
|
543
|
-
```typescript
|
|
544
|
-
export const useFetchAddPet = (
|
|
545
|
-
params: MaybeRef<AddPetRequest>,
|
|
546
|
-
options?: ApiRequestOptions<Pet>
|
|
547
|
-
) => {
|
|
548
|
-
const p = isRef(params) ? params : shallowRef(params);
|
|
549
|
-
return useApiRequest<Pet>('/pet', {
|
|
550
|
-
method: 'POST',
|
|
551
|
-
body: computed(() => p.value.pet),
|
|
552
|
-
...options,
|
|
553
|
-
});
|
|
554
|
-
};
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
**Handles:**
|
|
558
|
-
|
|
559
|
-
- Static URLs (`'/pet'`)
|
|
560
|
-
- Reactive URLs with path params via `() => \`/pet/${p.value.petId}\``
|
|
561
|
-
- Query params as `computed(() => ({ ... }))` for reactivity
|
|
562
|
-
- Body field extraction
|
|
563
|
-
- `MaybeRef<T>` params — plain objects are auto-wrapped in `shallowRef`
|
|
564
|
-
|
|
565
|
-
### generateComposableFile()
|
|
566
|
-
|
|
567
|
-
**File**: `src/generators/use-fetch/templates.ts`
|
|
568
|
-
|
|
569
|
-
**Signature:**
|
|
570
|
-
|
|
571
|
-
```typescript
|
|
572
|
-
function generateComposableFile(method: MethodInfo, apiImportPath: string): string;
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
**Purpose**: Generate complete file content for a composable.
|
|
576
|
-
|
|
577
|
-
**Returns**: Full file content (header + imports + function)
|
|
578
|
-
|
|
579
|
-
## Runtime APIs
|
|
580
|
-
|
|
581
|
-
### useApiRequest()
|
|
582
|
-
|
|
583
|
-
**File**: `src/generators/use-fetch/runtime/useApiRequest.ts`
|
|
584
|
-
|
|
585
|
-
**Signature:**
|
|
586
|
-
|
|
587
|
-
```typescript
|
|
588
|
-
export function useApiRequest<T>(
|
|
589
|
-
url: string | (() => string),
|
|
590
|
-
options?: ApiRequestOptions<T>
|
|
591
|
-
): UseFetchReturn<T>;
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
**Purpose**: Wrapper for `useFetch` with lifecycle callbacks.
|
|
595
|
-
|
|
596
|
-
**Features:**
|
|
597
|
-
|
|
598
|
-
- ✅ Execute callbacks in correct order
|
|
599
|
-
- ✅ Apply `pick` for field selection
|
|
600
|
-
- ✅ Apply `transform` for data transformation
|
|
601
|
-
- ✅ Merge global and local callbacks
|
|
602
|
-
- ✅ Support request modification via `onRequest`
|
|
603
|
-
- ✅ Full TypeScript type safety
|
|
604
|
-
|
|
605
|
-
**Implementation Details:**
|
|
606
|
-
|
|
607
|
-
```typescript
|
|
608
|
-
export function useApiRequest<T>(url: string, options?: ApiRequestOptions<T>) {
|
|
609
|
-
// 1. Extract skipGlobalCallbacks
|
|
610
|
-
const skipGlobalCallbacks = options?.skipGlobalCallbacks;
|
|
611
|
-
|
|
612
|
-
// 2. Merge callbacks
|
|
613
|
-
const mergedCallbacks = mergeCallbacks(
|
|
614
|
-
url,
|
|
615
|
-
{
|
|
616
|
-
onRequest: options?.onRequest,
|
|
617
|
-
onSuccess: options?.onSuccess,
|
|
618
|
-
onError: options?.onError,
|
|
619
|
-
onFinish: options?.onFinish,
|
|
620
|
-
},
|
|
621
|
-
skipGlobalCallbacks
|
|
622
|
-
);
|
|
623
|
-
|
|
624
|
-
// 3. Execute onRequest
|
|
625
|
-
let modifiedOptions = { ...options };
|
|
626
|
-
if (mergedCallbacks.onRequest) {
|
|
627
|
-
const ctx = { url, method: options?.method, ... };
|
|
628
|
-
const result = mergedCallbacks.onRequest(ctx);
|
|
629
|
-
if (result) {
|
|
630
|
-
modifiedOptions = applyRequestModifications(modifiedOptions, result);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// 4. Call useFetch
|
|
635
|
-
const result = useFetch<T>(url, modifiedOptions);
|
|
636
|
-
|
|
637
|
-
// 5. Watch for data, error, and pending state changes
|
|
638
|
-
watch(
|
|
639
|
-
() => [result.data.value, result.error.value, result.pending.value] as const,
|
|
640
|
-
async ([data, error, pending], [prevData, prevError, prevPending]) => {
|
|
641
|
-
if (data && data !== prevData) {
|
|
642
|
-
// Apply pick/transform
|
|
643
|
-
let processedData = data;
|
|
644
|
-
if (pick) processedData = applyPick(data, pick);
|
|
645
|
-
if (transform) processedData = transform(processedData);
|
|
646
|
-
|
|
647
|
-
// onSuccess
|
|
648
|
-
if (mergedCallbacks.onSuccess) {
|
|
649
|
-
await mergedCallbacks.onSuccess(processedData);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
if (error && error !== prevError) {
|
|
654
|
-
// onError
|
|
655
|
-
if (mergedCallbacks.onError) {
|
|
656
|
-
await mergedCallbacks.onError(error);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// onFinish when request completes (was pending, now not)
|
|
661
|
-
if (prevPending && !pending && mergedCallbacks.onFinish) {
|
|
662
|
-
await mergedCallbacks.onFinish({ data: ..., error: ..., success: !error });
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
{ immediate: true }
|
|
666
|
-
);
|
|
667
|
-
|
|
668
|
-
return result;
|
|
669
|
-
}
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
### useApiAsyncData()
|
|
673
|
-
|
|
674
|
-
**File**: `src/generators/use-async-data/runtime/useApiAsyncData.ts`
|
|
675
|
-
|
|
676
|
-
**Signature:**
|
|
677
|
-
|
|
678
|
-
```typescript
|
|
679
|
-
export function useApiAsyncData<T>(
|
|
680
|
-
key: string,
|
|
681
|
-
url: string | (() => string),
|
|
682
|
-
options?: ApiAsyncDataOptions<T>
|
|
683
|
-
): AsyncDataReturn<T>;
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
**Purpose**: Wrapper for `useAsyncData` with callbacks (normal version).
|
|
687
|
-
|
|
688
|
-
**Key Differences from useApiRequest:**
|
|
689
|
-
|
|
690
|
-
- Uses `$fetch` inside a `useAsyncData` handler instead of `useFetch` directly
|
|
691
|
-
- Callbacks execute in try/catch inside the fetch function
|
|
692
|
-
- Reactive watching via explicit `watchSources` (URL functions + body/params refs)
|
|
693
|
-
- Requires unique `key` parameter
|
|
694
|
-
- `watch: false` disables all reactive re-fetching
|
|
695
|
-
|
|
696
|
-
### useApiAsyncDataRaw()
|
|
697
|
-
|
|
698
|
-
**File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
|
|
699
|
-
|
|
700
|
-
**Signature:**
|
|
701
|
-
|
|
702
|
-
```typescript
|
|
703
|
-
export function useApiAsyncDataRaw<T>(
|
|
704
|
-
key: string,
|
|
705
|
-
url: string,
|
|
706
|
-
options?: ApiAsyncDataRawOptions<T>
|
|
707
|
-
): AsyncDataReturn<RawResponse<T>>;
|
|
708
|
-
```
|
|
709
|
-
|
|
710
|
-
**Purpose**: Wrapper with full response (data + headers + status).
|
|
711
|
-
|
|
712
|
-
**Key Differences:**
|
|
713
|
-
|
|
714
|
-
- Uses `$fetch.raw` instead of `$fetch`
|
|
715
|
-
- Returns `RawResponse<T>` instead of `T`
|
|
716
|
-
- `onSuccess` receives `(data, responseContext)` with headers/status
|
|
717
|
-
- `pick`/`transform` apply only to data, not full response
|
|
718
|
-
|
|
719
|
-
## Callback Interfaces
|
|
720
|
-
|
|
721
|
-
### Global Callbacks Config
|
|
722
|
-
|
|
723
|
-
**File**: `plugins/api-callbacks.ts` (copied to user's project)
|
|
724
|
-
|
|
725
|
-
**Structure:**
|
|
726
|
-
|
|
727
|
-
```typescript
|
|
728
|
-
export default defineNuxtPlugin(() => {
|
|
729
|
-
return {
|
|
730
|
-
provide: {
|
|
731
|
-
getGlobalApiCallbacks: () => ({
|
|
732
|
-
patterns: string[]; // Optional URL patterns
|
|
733
|
-
onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
|
|
734
|
-
onSuccess?: (data: any, ctx: SuccessContext) => void | false;
|
|
735
|
-
onError?: (error: any, ctx: ErrorContext) => void | false;
|
|
736
|
-
onFinish?: (ctx: FinishContext) => void;
|
|
737
|
-
}),
|
|
738
|
-
},
|
|
739
|
-
};
|
|
740
|
-
});
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
**Pattern Syntax:**
|
|
744
|
-
|
|
745
|
-
- `**` - Matches any number of path segments
|
|
746
|
-
- `*` - Matches single path segment
|
|
747
|
-
- No pattern - Applies to all URLs
|
|
748
|
-
|
|
749
|
-
**Examples:**
|
|
750
|
-
|
|
751
|
-
```typescript
|
|
752
|
-
patterns: [
|
|
753
|
-
'/api/auth/**', // All auth endpoints
|
|
754
|
-
'/api/users/**', // All user endpoints
|
|
755
|
-
'/api/admin/*/logs', // /api/admin/{id}/logs only
|
|
756
|
-
];
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
### Helper Functions
|
|
760
|
-
|
|
761
|
-
#### getGlobalCallbacks()
|
|
762
|
-
|
|
763
|
-
```typescript
|
|
764
|
-
function getGlobalCallbacks(): GlobalCallbacksConfig | null;
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
**Purpose**: Read global callbacks from Nuxt plugin.
|
|
768
|
-
|
|
769
|
-
**Returns**: Global config or `null` if plugin doesn't exist
|
|
770
|
-
|
|
771
|
-
#### shouldApplyGlobalCallback()
|
|
772
|
-
|
|
773
|
-
```typescript
|
|
774
|
-
function shouldApplyGlobalCallback(
|
|
775
|
-
callbackName: string,
|
|
776
|
-
skipConfig: SkipGlobalCallbacks | undefined,
|
|
777
|
-
url: string,
|
|
778
|
-
patterns?: string[]
|
|
779
|
-
): boolean;
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
**Purpose**: Determine if global callback should run.
|
|
783
|
-
|
|
784
|
-
**Checks:**
|
|
785
|
-
|
|
786
|
-
1. `skipConfig === true` → false
|
|
787
|
-
2. `skipConfig.includes(callbackName)` → false
|
|
788
|
-
3. URL matches patterns (if patterns exist) → true/false
|
|
789
|
-
4. Otherwise → true
|
|
790
|
-
|
|
791
|
-
#### mergeCallbacks()
|
|
792
|
-
|
|
793
|
-
```typescript
|
|
794
|
-
function mergeCallbacks(
|
|
795
|
-
url: string,
|
|
796
|
-
localCallbacks: Partial<GlobalCallbacksConfig>,
|
|
797
|
-
skipGlobalCallbacks?: SkipGlobalCallbacks
|
|
798
|
-
): Required<Omit<GlobalCallbacksConfig, 'patterns'>>;
|
|
799
|
-
```
|
|
800
|
-
|
|
801
|
-
**Purpose**: Merge global and local callbacks.
|
|
802
|
-
|
|
803
|
-
**Returns**: Object with all 4 callbacks (onRequest, onSuccess, onError, onFinish)
|
|
804
|
-
|
|
805
|
-
**Execution Order:**
|
|
806
|
-
|
|
807
|
-
1. Global callback runs first
|
|
808
|
-
2. If global returns `false`, local is skipped (except onFinish)
|
|
809
|
-
3. Local callback runs second
|
|
810
|
-
|
|
811
|
-
## Generated Composables
|
|
812
|
-
|
|
813
|
-
### useFetch Composables
|
|
814
|
-
|
|
815
|
-
**Pattern**: `useFetch[MethodName]`
|
|
816
|
-
|
|
817
|
-
**Example:**
|
|
818
|
-
|
|
819
|
-
```typescript
|
|
820
|
-
export const useFetchAddPet = (params: AddPetRequest, options?: ApiRequestOptions<Pet>) => {
|
|
821
|
-
return useApiRequest<Pet>('/pet', {
|
|
822
|
-
method: 'POST',
|
|
823
|
-
body: params.pet,
|
|
824
|
-
...options,
|
|
825
|
-
});
|
|
826
|
-
};
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
**Usage:**
|
|
830
|
-
|
|
831
|
-
```typescript
|
|
832
|
-
const { data, pending, error, refresh } = useFetchAddPet(
|
|
833
|
-
{ pet: { name: 'Fluffy', status: 'available' } },
|
|
834
|
-
{
|
|
835
|
-
onSuccess: (pet) => console.log('Pet added:', pet),
|
|
836
|
-
onError: (error) => console.error('Failed:', error),
|
|
837
|
-
}
|
|
838
|
-
);
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
### useAsyncData Composables
|
|
842
|
-
|
|
843
|
-
**Pattern**: `useAsyncData[MethodName]` & `useAsyncData[MethodName]Raw`
|
|
844
|
-
|
|
845
|
-
**Normal Version:**
|
|
846
|
-
|
|
847
|
-
```typescript
|
|
848
|
-
export const useAsyncDataGetPet = (params: GetPetRequest, options?: ApiAsyncDataOptions<Pet>) => {
|
|
849
|
-
return useApiAsyncData<Pet>('useAsyncDataGetPet', `/pet/${params.petId}`, {
|
|
850
|
-
method: 'GET',
|
|
851
|
-
...options,
|
|
852
|
-
});
|
|
853
|
-
};
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
**Raw Version:**
|
|
857
|
-
|
|
858
|
-
```typescript
|
|
859
|
-
export const useAsyncDataGetPetRaw = (
|
|
860
|
-
params: GetPetRequest,
|
|
861
|
-
options?: ApiAsyncDataRawOptions<Pet>
|
|
862
|
-
) => {
|
|
863
|
-
return useApiAsyncDataRaw<Pet>('useAsyncDataGetPetRaw', `/pet/${params.petId}`, {
|
|
864
|
-
method: 'GET',
|
|
865
|
-
...options,
|
|
866
|
-
});
|
|
867
|
-
};
|
|
868
|
-
```
|
|
869
|
-
|
|
870
|
-
**Usage:**
|
|
871
|
-
|
|
872
|
-
```typescript
|
|
873
|
-
// Normal
|
|
874
|
-
const { data, pending } = useAsyncDataGetPet({ petId: 123 });
|
|
875
|
-
// data.value: Pet
|
|
876
|
-
|
|
877
|
-
// Raw
|
|
878
|
-
const { data: rawData } = useAsyncDataGetPetRaw({ petId: 123 });
|
|
879
|
-
// rawData.value: { data: Pet, headers: Headers, status: 200, statusText: 'OK' }
|
|
880
|
-
const pet = rawData.value?.data;
|
|
881
|
-
const token = rawData.value?.headers.get('X-Auth-Token');
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
---
|
|
885
|
-
|
|
886
|
-
**Next**: [Development Guide](./DEVELOPMENT.md) | [Troubleshooting](./TROUBLESHOOTING.md)
|
|
1
|
+
# 📖 API Reference - Complete Technical Reference
|
|
2
|
+
|
|
3
|
+
> **Purpose**: Complete technical documentation of all interfaces, functions, and configuration options.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [CLI Commands](#cli-commands)
|
|
8
|
+
- [Configuration](#configuration)
|
|
9
|
+
- [Interfaces & Types](#interfaces--types)
|
|
10
|
+
- [Parser API](#parser-api)
|
|
11
|
+
- [Template API](#template-api)
|
|
12
|
+
- [Runtime APIs](#runtime-apis)
|
|
13
|
+
- [Callback Interfaces](#callback-interfaces)
|
|
14
|
+
- [Generated Composables](#generated-composables)
|
|
15
|
+
|
|
16
|
+
## CLI Commands
|
|
17
|
+
|
|
18
|
+
### `generate`
|
|
19
|
+
|
|
20
|
+
Generate Nuxt composables from OpenAPI/Swagger specifications.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
nxh generate [options]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Options:**
|
|
27
|
+
|
|
28
|
+
| Option | Alias | Type | Description |
|
|
29
|
+
| ----------------------- | ----- | -------- | -------------------------- |
|
|
30
|
+
| `--input <file>` | `-i` | `string` | OpenAPI/Swagger file path |
|
|
31
|
+
| `--output <directory>` | `-o` | `string` | Output directory |
|
|
32
|
+
| `--composables <types>` | `-c` | `string` | Comma-separated types list |
|
|
33
|
+
|
|
34
|
+
**Composable Types:**
|
|
35
|
+
|
|
36
|
+
- `useFetch` - Nuxt 3 useFetch composables
|
|
37
|
+
- `useAsyncData` - Nuxt 3 useAsyncData composables (normal + raw)
|
|
38
|
+
- `nuxt-server` - Nuxt server routes with BFF pattern
|
|
39
|
+
|
|
40
|
+
**Interactive Mode:**
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
nxh generate
|
|
44
|
+
# Prompts for input file, output directory, and composable types
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Non-Interactive Mode:**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
nxh generate -i swagger.yaml -o ./swagger
|
|
51
|
+
# Still prompts for composable types
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Fully Automated:**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
echo "useFetch" | nxh generate -i swagger.yaml -o ./swagger
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Exit Codes:**
|
|
61
|
+
|
|
62
|
+
- `0` - Success
|
|
63
|
+
- `1` - Error (missing files, parsing errors, etc.)
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
### OpenAPI Generator Config
|
|
68
|
+
|
|
69
|
+
Located in: `openapitools.json` (auto-created if missing)
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"generator-cli": {
|
|
74
|
+
"version": "7.14.0",
|
|
75
|
+
"generators": {
|
|
76
|
+
"typescript": {
|
|
77
|
+
"generatorName": "typescript-fetch",
|
|
78
|
+
"output": "./swagger"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Key Settings:**
|
|
86
|
+
|
|
87
|
+
- `generatorName`: Must be `typescript-fetch` (only supported target)
|
|
88
|
+
- `output`: Where OpenAPI Generator writes TypeScript files
|
|
89
|
+
- `version`: OpenAPI Generator CLI version
|
|
90
|
+
|
|
91
|
+
### Prettier Config
|
|
92
|
+
|
|
93
|
+
Generated files are formatted with Prettier using these settings:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
{
|
|
97
|
+
parser: 'typescript',
|
|
98
|
+
singleQuote: true,
|
|
99
|
+
semi: true,
|
|
100
|
+
trailingComma: 'es5',
|
|
101
|
+
printWidth: 100
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Interfaces & Types
|
|
106
|
+
|
|
107
|
+
### MethodInfo
|
|
108
|
+
|
|
109
|
+
**File**: `src/generators/shared/types.ts`
|
|
110
|
+
|
|
111
|
+
**Purpose**: Represents a single API endpoint with all information needed for code generation.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export interface MethodInfo {
|
|
115
|
+
// Basic info
|
|
116
|
+
name: string; // e.g., 'addPet'
|
|
117
|
+
composableName: string; // e.g., 'useFetchAddPet'
|
|
118
|
+
path: string; // e.g., '/pet' or '/pet/{petId}'
|
|
119
|
+
httpMethod: string; // 'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH'
|
|
120
|
+
|
|
121
|
+
// Type information
|
|
122
|
+
requestType?: string; // e.g., 'AddPetRequest'
|
|
123
|
+
responseType: string; // e.g., 'Pet'
|
|
124
|
+
|
|
125
|
+
// Request details
|
|
126
|
+
pathParams: string[]; // e.g., ['petId']
|
|
127
|
+
queryParams: string[]; // e.g., ['status', 'limit']
|
|
128
|
+
headers: Record<string, string>; // Default headers to include
|
|
129
|
+
hasBody: boolean; // true if method uses a request body
|
|
130
|
+
bodyField?: string; // e.g., 'pet' (from requestParameters['pet'])
|
|
131
|
+
hasQueryParams: boolean; // true if has query parameters
|
|
132
|
+
|
|
133
|
+
// Metadata
|
|
134
|
+
description?: string; // JSDoc description from the method
|
|
135
|
+
hasRawMethod: boolean; // true if xxxRaw() method exists
|
|
136
|
+
rawMethodName?: string; // e.g., 'addPetRaw'
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Example:**
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
{
|
|
144
|
+
name: 'addPet',
|
|
145
|
+
composableName: 'useFetchAddPet',
|
|
146
|
+
path: '/pet',
|
|
147
|
+
httpMethod: 'POST',
|
|
148
|
+
requestType: 'AddPetRequest',
|
|
149
|
+
responseType: 'Pet',
|
|
150
|
+
pathParams: [],
|
|
151
|
+
queryParams: [],
|
|
152
|
+
headers: {},
|
|
153
|
+
hasBody: true,
|
|
154
|
+
bodyField: 'pet',
|
|
155
|
+
hasQueryParams: false,
|
|
156
|
+
hasRawMethod: false
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### ApiClassInfo
|
|
161
|
+
|
|
162
|
+
**File**: `src/generators/shared/types.ts`
|
|
163
|
+
|
|
164
|
+
**Purpose**: Groups all methods from a single API class.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
export interface ApiClassInfo {
|
|
168
|
+
className: string; // e.g., 'PetApi'
|
|
169
|
+
methods: MethodInfo[]; // All methods from this API class
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### RequestContext
|
|
174
|
+
|
|
175
|
+
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
176
|
+
|
|
177
|
+
**Purpose**: Context object passed to `onRequest` callback.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
export interface RequestContext {
|
|
181
|
+
url: string; // Request URL
|
|
182
|
+
method: string; // HTTP method
|
|
183
|
+
body?: any; // Request body
|
|
184
|
+
headers?: Record<string, string>; // Headers
|
|
185
|
+
query?: Record<string, any>; // Query params
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Usage:**
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
onRequest: (ctx: RequestContext) => {
|
|
193
|
+
console.log(`Making ${ctx.method} request to ${ctx.url}`);
|
|
194
|
+
};
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### ModifiedRequestContext
|
|
198
|
+
|
|
199
|
+
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
200
|
+
|
|
201
|
+
**Purpose**: Returned by `onRequest` to modify the request.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
export interface ModifiedRequestContext {
|
|
205
|
+
headers?: Record<string, string>; // Modified headers
|
|
206
|
+
query?: Record<string, any>; // Modified query params
|
|
207
|
+
body?: any; // Modified body
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Usage:**
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
onRequest: (ctx) => {
|
|
215
|
+
return {
|
|
216
|
+
headers: {
|
|
217
|
+
...ctx.headers,
|
|
218
|
+
Authorization: `Bearer ${token}`,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### SuccessContext
|
|
225
|
+
|
|
226
|
+
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
227
|
+
|
|
228
|
+
**Purpose**: Context passed to `onSuccess` callback. Different for normal vs raw.
|
|
229
|
+
|
|
230
|
+
**Normal Version:**
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
export interface SuccessContext {
|
|
234
|
+
url: string;
|
|
235
|
+
method: string;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Raw Version:**
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
export interface SuccessContext {
|
|
243
|
+
url: string;
|
|
244
|
+
method: string;
|
|
245
|
+
headers: Headers; // Response headers
|
|
246
|
+
status: number; // HTTP status code
|
|
247
|
+
statusText: string; // Status text
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Usage:**
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
// Normal
|
|
255
|
+
onSuccess: (data: Pet, ctx: SuccessContext) => {
|
|
256
|
+
console.log(`Got pet from ${ctx.url}`);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Raw
|
|
260
|
+
onSuccess: (data: Pet, ctx: SuccessContext) => {
|
|
261
|
+
const token = ctx.headers.get('X-Auth-Token');
|
|
262
|
+
console.log(`Status: ${ctx.status}`);
|
|
263
|
+
};
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### ErrorContext
|
|
267
|
+
|
|
268
|
+
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
269
|
+
|
|
270
|
+
**Purpose**: Context passed to `onError` callback.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
export interface ErrorContext {
|
|
274
|
+
url: string;
|
|
275
|
+
method: string;
|
|
276
|
+
status?: number; // HTTP status if available
|
|
277
|
+
message: string; // Error message
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### FinishContext
|
|
282
|
+
|
|
283
|
+
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
284
|
+
|
|
285
|
+
**Purpose**: Context passed to `onFinish` callback.
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
export interface FinishContext {
|
|
289
|
+
success: boolean; // true if succeeded, false if error
|
|
290
|
+
data?: any; // Data if success
|
|
291
|
+
error?: any; // Error if failed
|
|
292
|
+
duration?: number; // Optional: request duration in ms
|
|
293
|
+
url: string;
|
|
294
|
+
method: string;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### ApiRequestOptions
|
|
299
|
+
|
|
300
|
+
**File**: `src/generators/shared/runtime/apiHelpers.ts`
|
|
301
|
+
|
|
302
|
+
**Purpose**: Options for all `useApiRequest` calls.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
export interface ApiRequestOptions<T> {
|
|
306
|
+
// useFetch native options
|
|
307
|
+
method?: string;
|
|
308
|
+
body?: any;
|
|
309
|
+
headers?: Record<string, string>;
|
|
310
|
+
query?: Record<string, any>;
|
|
311
|
+
baseURL?: string;
|
|
312
|
+
server?: boolean;
|
|
313
|
+
lazy?: boolean;
|
|
314
|
+
immediate?: boolean;
|
|
315
|
+
|
|
316
|
+
// Custom features
|
|
317
|
+
pick?: string[]; // Dot notation field selection
|
|
318
|
+
transform?: (data: T) => any; // Transform response
|
|
319
|
+
|
|
320
|
+
// Lifecycle callbacks
|
|
321
|
+
onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
|
|
322
|
+
onSuccess?: (data: T, ctx: SuccessContext) => void | false;
|
|
323
|
+
onError?: (error: any, ctx: ErrorContext) => void | false;
|
|
324
|
+
onFinish?: (ctx: FinishContext) => void;
|
|
325
|
+
|
|
326
|
+
// Global callbacks control
|
|
327
|
+
skipGlobalCallbacks?: boolean | Array<'onRequest' | 'onSuccess' | 'onError' | 'onFinish'>;
|
|
328
|
+
|
|
329
|
+
// Any other useFetch options
|
|
330
|
+
[key: string]: any;
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### ApiAsyncDataOptions
|
|
335
|
+
|
|
336
|
+
**File**: `src/generators/use-async-data/runtime/useApiAsyncData.ts`
|
|
337
|
+
|
|
338
|
+
**Purpose**: Options for `useAsyncData` composables (normal version).
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
export interface ApiAsyncDataOptions<T> {
|
|
342
|
+
// useAsyncData native options
|
|
343
|
+
server?: boolean;
|
|
344
|
+
lazy?: boolean;
|
|
345
|
+
immediate?: boolean;
|
|
346
|
+
dedupe?: 'cancel' | 'defer';
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Disable automatic refresh when reactive params change.
|
|
350
|
+
* Set to false to prevent re-fetching when params/url refs update.
|
|
351
|
+
* @default true
|
|
352
|
+
*/
|
|
353
|
+
watch?: boolean;
|
|
354
|
+
|
|
355
|
+
// Custom features
|
|
356
|
+
pick?: string[];
|
|
357
|
+
transform?: (data: T) => any;
|
|
358
|
+
|
|
359
|
+
// Lifecycle callbacks
|
|
360
|
+
onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
|
|
361
|
+
onSuccess?: (data: T) => void | false;
|
|
362
|
+
onError?: (error: any) => void | false;
|
|
363
|
+
onFinish?: (ctx: FinishContext) => void;
|
|
364
|
+
|
|
365
|
+
// Global callbacks control
|
|
366
|
+
skipGlobalCallbacks?: boolean | string[];
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### ApiAsyncDataRawOptions
|
|
371
|
+
|
|
372
|
+
**File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
|
|
373
|
+
|
|
374
|
+
**Purpose**: Options for `useAsyncData` raw composables.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
export interface ApiAsyncDataRawOptions<T> extends ApiAsyncDataOptions<T> {
|
|
378
|
+
// Same as ApiAsyncDataOptions
|
|
379
|
+
// But onSuccess signature is different:
|
|
380
|
+
// onSuccess?: (data: T, ctx: SuccessContext) => void | false
|
|
381
|
+
// where ctx includes: headers, status, statusText
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### RawResponse
|
|
386
|
+
|
|
387
|
+
**File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
|
|
388
|
+
|
|
389
|
+
**Purpose**: Response structure for raw composables.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
export interface RawResponse<T> {
|
|
393
|
+
data: T; // Actual response data
|
|
394
|
+
headers: Headers; // Response headers
|
|
395
|
+
status: number; // HTTP status code
|
|
396
|
+
statusText: string; // Status text
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Usage:**
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const { data, pending } = useAsyncDataGetPetRaw({ petId: 123 });
|
|
404
|
+
// data.value = { data: Pet, headers: Headers, status: 200, statusText: 'OK' }
|
|
405
|
+
|
|
406
|
+
const pet = data.value?.data;
|
|
407
|
+
const authToken = data.value?.headers.get('X-Auth-Token');
|
|
408
|
+
const wasCreated = data.value?.status === 201;
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Parser API
|
|
412
|
+
|
|
413
|
+
### extractMethodInfo()
|
|
414
|
+
|
|
415
|
+
**File**: `src/generators/use-fetch/parser.ts`
|
|
416
|
+
|
|
417
|
+
**Signature:**
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
function extractMethodInfo(method: MethodDeclaration, sourceFile: SourceFile): MethodInfo | null;
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Purpose**: Extract API information from a TypeScript method.
|
|
424
|
+
|
|
425
|
+
**Parameters:**
|
|
426
|
+
|
|
427
|
+
- `method`: ts-morph MethodDeclaration node
|
|
428
|
+
- `sourceFile`: ts-morph SourceFile for context
|
|
429
|
+
|
|
430
|
+
**Returns**: `MethodInfo` object or `null` if extraction fails
|
|
431
|
+
|
|
432
|
+
**Algorithm:**
|
|
433
|
+
|
|
434
|
+
1. Extract method name
|
|
435
|
+
2. Find `xxxRequestOpts()` method (contains HTTP details)
|
|
436
|
+
3. Parse return statement to get path, method, body, headers
|
|
437
|
+
4. Extract parameter types
|
|
438
|
+
5. Extract return type (response type)
|
|
439
|
+
6. Generate composable name and file name
|
|
440
|
+
|
|
441
|
+
**Error Handling**: Returns `null` on errors (logged to console)
|
|
442
|
+
|
|
443
|
+
### getApiFiles()
|
|
444
|
+
|
|
445
|
+
**File**: `src/generators/use-fetch/parser.ts`
|
|
446
|
+
|
|
447
|
+
**Signature:**
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
function getApiFiles(inputDir: string): string[];
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Purpose**: Find all API files in the `apis/` subdirectory.
|
|
454
|
+
|
|
455
|
+
**Parameters:**
|
|
456
|
+
|
|
457
|
+
- `inputDir`: Path to the root output directory (e.g., `./swagger`). The function appends `/apis` internally.
|
|
458
|
+
|
|
459
|
+
**Returns**: Array of absolute file paths
|
|
460
|
+
|
|
461
|
+
**Filters**: Only includes `*.ts` files that are not `index.ts`
|
|
462
|
+
|
|
463
|
+
### parseApiFile()
|
|
464
|
+
|
|
465
|
+
**File**: `src/generators/use-fetch/parser.ts`
|
|
466
|
+
|
|
467
|
+
**Signature:**
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
function parseApiFile(filePath: string): ApiClassInfo;
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Purpose**: Parse a single API file and extract all methods.
|
|
474
|
+
|
|
475
|
+
**Parameters:**
|
|
476
|
+
|
|
477
|
+
- `filePath`: Absolute path to API file
|
|
478
|
+
|
|
479
|
+
**Returns**: `ApiClassInfo` for the first class found in the file
|
|
480
|
+
|
|
481
|
+
**Algorithm:**
|
|
482
|
+
|
|
483
|
+
1. Create ts-morph Project
|
|
484
|
+
2. Add source file
|
|
485
|
+
3. Find all classes
|
|
486
|
+
4. For each class:
|
|
487
|
+
- Extract all methods
|
|
488
|
+
- Call `extractMethodInfo()` for each
|
|
489
|
+
- Filter out nulls
|
|
490
|
+
5. Return array of ApiClassInfo
|
|
491
|
+
|
|
492
|
+
## Template API
|
|
493
|
+
|
|
494
|
+
### generateFileHeader()
|
|
495
|
+
|
|
496
|
+
**File**: `src/generators/use-fetch/templates.ts`
|
|
497
|
+
|
|
498
|
+
**Signature:**
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
function generateFileHeader(): string;
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Returns**: TypeScript header comment
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// @ts-nocheck - This file runs in user's Nuxt project with different tsconfig
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### generateImports()
|
|
511
|
+
|
|
512
|
+
**File**: `src/generators/use-fetch/templates.ts`
|
|
513
|
+
|
|
514
|
+
**Signature:**
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
function generateImports(method: MethodInfo, apiImportPath: string): string;
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Purpose**: Generate import statements for composable file.
|
|
521
|
+
|
|
522
|
+
**Returns:**
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
import type { AddPetRequest, Pet } from '../apis/PetApi';
|
|
526
|
+
import { useApiRequest, type ApiRequestOptions } from '../runtime/useApiRequest';
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### generateFunctionBody()
|
|
530
|
+
|
|
531
|
+
**File**: `src/generators/use-fetch/templates.ts`
|
|
532
|
+
|
|
533
|
+
**Signature:**
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
function generateFunctionBody(method: MethodInfo): string;
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
**Purpose**: Generate composable function code.
|
|
540
|
+
|
|
541
|
+
**Returns:**
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
export const useFetchAddPet = (
|
|
545
|
+
params: MaybeRef<AddPetRequest>,
|
|
546
|
+
options?: ApiRequestOptions<Pet>
|
|
547
|
+
) => {
|
|
548
|
+
const p = isRef(params) ? params : shallowRef(params);
|
|
549
|
+
return useApiRequest<Pet>('/pet', {
|
|
550
|
+
method: 'POST',
|
|
551
|
+
body: computed(() => p.value.pet),
|
|
552
|
+
...options,
|
|
553
|
+
});
|
|
554
|
+
};
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Handles:**
|
|
558
|
+
|
|
559
|
+
- Static URLs (`'/pet'`)
|
|
560
|
+
- Reactive URLs with path params via `() => \`/pet/${p.value.petId}\``
|
|
561
|
+
- Query params as `computed(() => ({ ... }))` for reactivity
|
|
562
|
+
- Body field extraction
|
|
563
|
+
- `MaybeRef<T>` params — plain objects are auto-wrapped in `shallowRef`
|
|
564
|
+
|
|
565
|
+
### generateComposableFile()
|
|
566
|
+
|
|
567
|
+
**File**: `src/generators/use-fetch/templates.ts`
|
|
568
|
+
|
|
569
|
+
**Signature:**
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
function generateComposableFile(method: MethodInfo, apiImportPath: string): string;
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
**Purpose**: Generate complete file content for a composable.
|
|
576
|
+
|
|
577
|
+
**Returns**: Full file content (header + imports + function)
|
|
578
|
+
|
|
579
|
+
## Runtime APIs
|
|
580
|
+
|
|
581
|
+
### useApiRequest()
|
|
582
|
+
|
|
583
|
+
**File**: `src/generators/use-fetch/runtime/useApiRequest.ts`
|
|
584
|
+
|
|
585
|
+
**Signature:**
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
export function useApiRequest<T>(
|
|
589
|
+
url: string | (() => string),
|
|
590
|
+
options?: ApiRequestOptions<T>
|
|
591
|
+
): UseFetchReturn<T>;
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
**Purpose**: Wrapper for `useFetch` with lifecycle callbacks.
|
|
595
|
+
|
|
596
|
+
**Features:**
|
|
597
|
+
|
|
598
|
+
- ✅ Execute callbacks in correct order
|
|
599
|
+
- ✅ Apply `pick` for field selection
|
|
600
|
+
- ✅ Apply `transform` for data transformation
|
|
601
|
+
- ✅ Merge global and local callbacks
|
|
602
|
+
- ✅ Support request modification via `onRequest`
|
|
603
|
+
- ✅ Full TypeScript type safety
|
|
604
|
+
|
|
605
|
+
**Implementation Details:**
|
|
606
|
+
|
|
607
|
+
```typescript
|
|
608
|
+
export function useApiRequest<T>(url: string, options?: ApiRequestOptions<T>) {
|
|
609
|
+
// 1. Extract skipGlobalCallbacks
|
|
610
|
+
const skipGlobalCallbacks = options?.skipGlobalCallbacks;
|
|
611
|
+
|
|
612
|
+
// 2. Merge callbacks
|
|
613
|
+
const mergedCallbacks = mergeCallbacks(
|
|
614
|
+
url,
|
|
615
|
+
{
|
|
616
|
+
onRequest: options?.onRequest,
|
|
617
|
+
onSuccess: options?.onSuccess,
|
|
618
|
+
onError: options?.onError,
|
|
619
|
+
onFinish: options?.onFinish,
|
|
620
|
+
},
|
|
621
|
+
skipGlobalCallbacks
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
// 3. Execute onRequest
|
|
625
|
+
let modifiedOptions = { ...options };
|
|
626
|
+
if (mergedCallbacks.onRequest) {
|
|
627
|
+
const ctx = { url, method: options?.method, ... };
|
|
628
|
+
const result = mergedCallbacks.onRequest(ctx);
|
|
629
|
+
if (result) {
|
|
630
|
+
modifiedOptions = applyRequestModifications(modifiedOptions, result);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// 4. Call useFetch
|
|
635
|
+
const result = useFetch<T>(url, modifiedOptions);
|
|
636
|
+
|
|
637
|
+
// 5. Watch for data, error, and pending state changes
|
|
638
|
+
watch(
|
|
639
|
+
() => [result.data.value, result.error.value, result.pending.value] as const,
|
|
640
|
+
async ([data, error, pending], [prevData, prevError, prevPending]) => {
|
|
641
|
+
if (data && data !== prevData) {
|
|
642
|
+
// Apply pick/transform
|
|
643
|
+
let processedData = data;
|
|
644
|
+
if (pick) processedData = applyPick(data, pick);
|
|
645
|
+
if (transform) processedData = transform(processedData);
|
|
646
|
+
|
|
647
|
+
// onSuccess
|
|
648
|
+
if (mergedCallbacks.onSuccess) {
|
|
649
|
+
await mergedCallbacks.onSuccess(processedData);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (error && error !== prevError) {
|
|
654
|
+
// onError
|
|
655
|
+
if (mergedCallbacks.onError) {
|
|
656
|
+
await mergedCallbacks.onError(error);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// onFinish when request completes (was pending, now not)
|
|
661
|
+
if (prevPending && !pending && mergedCallbacks.onFinish) {
|
|
662
|
+
await mergedCallbacks.onFinish({ data: ..., error: ..., success: !error });
|
|
663
|
+
}
|
|
664
|
+
},
|
|
665
|
+
{ immediate: true }
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### useApiAsyncData()
|
|
673
|
+
|
|
674
|
+
**File**: `src/generators/use-async-data/runtime/useApiAsyncData.ts`
|
|
675
|
+
|
|
676
|
+
**Signature:**
|
|
677
|
+
|
|
678
|
+
```typescript
|
|
679
|
+
export function useApiAsyncData<T>(
|
|
680
|
+
key: string,
|
|
681
|
+
url: string | (() => string),
|
|
682
|
+
options?: ApiAsyncDataOptions<T>
|
|
683
|
+
): AsyncDataReturn<T>;
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
**Purpose**: Wrapper for `useAsyncData` with callbacks (normal version).
|
|
687
|
+
|
|
688
|
+
**Key Differences from useApiRequest:**
|
|
689
|
+
|
|
690
|
+
- Uses `$fetch` inside a `useAsyncData` handler instead of `useFetch` directly
|
|
691
|
+
- Callbacks execute in try/catch inside the fetch function
|
|
692
|
+
- Reactive watching via explicit `watchSources` (URL functions + body/params refs)
|
|
693
|
+
- Requires unique `key` parameter
|
|
694
|
+
- `watch: false` disables all reactive re-fetching
|
|
695
|
+
|
|
696
|
+
### useApiAsyncDataRaw()
|
|
697
|
+
|
|
698
|
+
**File**: `src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts`
|
|
699
|
+
|
|
700
|
+
**Signature:**
|
|
701
|
+
|
|
702
|
+
```typescript
|
|
703
|
+
export function useApiAsyncDataRaw<T>(
|
|
704
|
+
key: string,
|
|
705
|
+
url: string,
|
|
706
|
+
options?: ApiAsyncDataRawOptions<T>
|
|
707
|
+
): AsyncDataReturn<RawResponse<T>>;
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**Purpose**: Wrapper with full response (data + headers + status).
|
|
711
|
+
|
|
712
|
+
**Key Differences:**
|
|
713
|
+
|
|
714
|
+
- Uses `$fetch.raw` instead of `$fetch`
|
|
715
|
+
- Returns `RawResponse<T>` instead of `T`
|
|
716
|
+
- `onSuccess` receives `(data, responseContext)` with headers/status
|
|
717
|
+
- `pick`/`transform` apply only to data, not full response
|
|
718
|
+
|
|
719
|
+
## Callback Interfaces
|
|
720
|
+
|
|
721
|
+
### Global Callbacks Config
|
|
722
|
+
|
|
723
|
+
**File**: `plugins/api-callbacks.ts` (copied to user's project)
|
|
724
|
+
|
|
725
|
+
**Structure:**
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
export default defineNuxtPlugin(() => {
|
|
729
|
+
return {
|
|
730
|
+
provide: {
|
|
731
|
+
getGlobalApiCallbacks: () => ({
|
|
732
|
+
patterns: string[]; // Optional URL patterns
|
|
733
|
+
onRequest?: (ctx: RequestContext) => void | ModifiedRequestContext | false;
|
|
734
|
+
onSuccess?: (data: any, ctx: SuccessContext) => void | false;
|
|
735
|
+
onError?: (error: any, ctx: ErrorContext) => void | false;
|
|
736
|
+
onFinish?: (ctx: FinishContext) => void;
|
|
737
|
+
}),
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
});
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
**Pattern Syntax:**
|
|
744
|
+
|
|
745
|
+
- `**` - Matches any number of path segments
|
|
746
|
+
- `*` - Matches single path segment
|
|
747
|
+
- No pattern - Applies to all URLs
|
|
748
|
+
|
|
749
|
+
**Examples:**
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
patterns: [
|
|
753
|
+
'/api/auth/**', // All auth endpoints
|
|
754
|
+
'/api/users/**', // All user endpoints
|
|
755
|
+
'/api/admin/*/logs', // /api/admin/{id}/logs only
|
|
756
|
+
];
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Helper Functions
|
|
760
|
+
|
|
761
|
+
#### getGlobalCallbacks()
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
function getGlobalCallbacks(): GlobalCallbacksConfig | null;
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
**Purpose**: Read global callbacks from Nuxt plugin.
|
|
768
|
+
|
|
769
|
+
**Returns**: Global config or `null` if plugin doesn't exist
|
|
770
|
+
|
|
771
|
+
#### shouldApplyGlobalCallback()
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
function shouldApplyGlobalCallback(
|
|
775
|
+
callbackName: string,
|
|
776
|
+
skipConfig: SkipGlobalCallbacks | undefined,
|
|
777
|
+
url: string,
|
|
778
|
+
patterns?: string[]
|
|
779
|
+
): boolean;
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
**Purpose**: Determine if global callback should run.
|
|
783
|
+
|
|
784
|
+
**Checks:**
|
|
785
|
+
|
|
786
|
+
1. `skipConfig === true` → false
|
|
787
|
+
2. `skipConfig.includes(callbackName)` → false
|
|
788
|
+
3. URL matches patterns (if patterns exist) → true/false
|
|
789
|
+
4. Otherwise → true
|
|
790
|
+
|
|
791
|
+
#### mergeCallbacks()
|
|
792
|
+
|
|
793
|
+
```typescript
|
|
794
|
+
function mergeCallbacks(
|
|
795
|
+
url: string,
|
|
796
|
+
localCallbacks: Partial<GlobalCallbacksConfig>,
|
|
797
|
+
skipGlobalCallbacks?: SkipGlobalCallbacks
|
|
798
|
+
): Required<Omit<GlobalCallbacksConfig, 'patterns'>>;
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
**Purpose**: Merge global and local callbacks.
|
|
802
|
+
|
|
803
|
+
**Returns**: Object with all 4 callbacks (onRequest, onSuccess, onError, onFinish)
|
|
804
|
+
|
|
805
|
+
**Execution Order:**
|
|
806
|
+
|
|
807
|
+
1. Global callback runs first
|
|
808
|
+
2. If global returns `false`, local is skipped (except onFinish)
|
|
809
|
+
3. Local callback runs second
|
|
810
|
+
|
|
811
|
+
## Generated Composables
|
|
812
|
+
|
|
813
|
+
### useFetch Composables
|
|
814
|
+
|
|
815
|
+
**Pattern**: `useFetch[MethodName]`
|
|
816
|
+
|
|
817
|
+
**Example:**
|
|
818
|
+
|
|
819
|
+
```typescript
|
|
820
|
+
export const useFetchAddPet = (params: AddPetRequest, options?: ApiRequestOptions<Pet>) => {
|
|
821
|
+
return useApiRequest<Pet>('/pet', {
|
|
822
|
+
method: 'POST',
|
|
823
|
+
body: params.pet,
|
|
824
|
+
...options,
|
|
825
|
+
});
|
|
826
|
+
};
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
**Usage:**
|
|
830
|
+
|
|
831
|
+
```typescript
|
|
832
|
+
const { data, pending, error, refresh } = useFetchAddPet(
|
|
833
|
+
{ pet: { name: 'Fluffy', status: 'available' } },
|
|
834
|
+
{
|
|
835
|
+
onSuccess: (pet) => console.log('Pet added:', pet),
|
|
836
|
+
onError: (error) => console.error('Failed:', error),
|
|
837
|
+
}
|
|
838
|
+
);
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### useAsyncData Composables
|
|
842
|
+
|
|
843
|
+
**Pattern**: `useAsyncData[MethodName]` & `useAsyncData[MethodName]Raw`
|
|
844
|
+
|
|
845
|
+
**Normal Version:**
|
|
846
|
+
|
|
847
|
+
```typescript
|
|
848
|
+
export const useAsyncDataGetPet = (params: GetPetRequest, options?: ApiAsyncDataOptions<Pet>) => {
|
|
849
|
+
return useApiAsyncData<Pet>('useAsyncDataGetPet', `/pet/${params.petId}`, {
|
|
850
|
+
method: 'GET',
|
|
851
|
+
...options,
|
|
852
|
+
});
|
|
853
|
+
};
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
**Raw Version:**
|
|
857
|
+
|
|
858
|
+
```typescript
|
|
859
|
+
export const useAsyncDataGetPetRaw = (
|
|
860
|
+
params: GetPetRequest,
|
|
861
|
+
options?: ApiAsyncDataRawOptions<Pet>
|
|
862
|
+
) => {
|
|
863
|
+
return useApiAsyncDataRaw<Pet>('useAsyncDataGetPetRaw', `/pet/${params.petId}`, {
|
|
864
|
+
method: 'GET',
|
|
865
|
+
...options,
|
|
866
|
+
});
|
|
867
|
+
};
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
**Usage:**
|
|
871
|
+
|
|
872
|
+
```typescript
|
|
873
|
+
// Normal
|
|
874
|
+
const { data, pending } = useAsyncDataGetPet({ petId: 123 });
|
|
875
|
+
// data.value: Pet
|
|
876
|
+
|
|
877
|
+
// Raw
|
|
878
|
+
const { data: rawData } = useAsyncDataGetPetRaw({ petId: 123 });
|
|
879
|
+
// rawData.value: { data: Pet, headers: Headers, status: 200, statusText: 'OK' }
|
|
880
|
+
const pet = rawData.value?.data;
|
|
881
|
+
const token = rawData.value?.headers.get('X-Auth-Token');
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
---
|
|
885
|
+
|
|
886
|
+
**Next**: [Development Guide](./DEVELOPMENT.md) | [Troubleshooting](./TROUBLESHOOTING.md)
|