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.
Files changed (60) hide show
  1. package/.editorconfig +26 -26
  2. package/.prettierignore +17 -17
  3. package/CONTRIBUTING.md +291 -291
  4. package/INSTRUCTIONS.md +327 -327
  5. package/LICENSE +202 -202
  6. package/README.md +231 -231
  7. package/dist/cli/config.d.ts +9 -2
  8. package/dist/cli/config.js +1 -1
  9. package/dist/cli/logo.js +5 -5
  10. package/dist/cli/messages.d.ts +1 -0
  11. package/dist/cli/messages.js +2 -0
  12. package/dist/cli/prompts.d.ts +5 -0
  13. package/dist/cli/prompts.js +12 -0
  14. package/dist/cli/types.d.ts +1 -1
  15. package/dist/generators/components/connector-generator/templates.js +12 -12
  16. package/dist/generators/use-async-data/templates.js +17 -17
  17. package/dist/generators/use-fetch/templates.js +14 -14
  18. package/dist/index.js +39 -27
  19. package/dist/module/index.js +19 -0
  20. package/dist/module/types.d.ts +7 -0
  21. package/docs/API-REFERENCE.md +886 -886
  22. package/docs/generated-components.md +615 -0
  23. package/docs/headless-composables-ui.md +569 -0
  24. package/eslint.config.js +85 -85
  25. package/package.json +8 -2
  26. package/src/cli/config.ts +147 -140
  27. package/src/cli/logger.ts +124 -124
  28. package/src/cli/logo.ts +25 -25
  29. package/src/cli/messages.ts +4 -0
  30. package/src/cli/prompts.ts +14 -1
  31. package/src/cli/types.ts +50 -50
  32. package/src/generators/components/connector-generator/generator.ts +138 -0
  33. package/src/generators/components/connector-generator/templates.ts +254 -0
  34. package/src/generators/components/connector-generator/types.ts +34 -0
  35. package/src/generators/components/schema-analyzer/index.ts +44 -0
  36. package/src/generators/components/schema-analyzer/intent-detector.ts +187 -0
  37. package/src/generators/components/schema-analyzer/openapi-reader.ts +96 -0
  38. package/src/generators/components/schema-analyzer/resource-grouper.ts +166 -0
  39. package/src/generators/components/schema-analyzer/schema-field-mapper.ts +268 -0
  40. package/src/generators/components/schema-analyzer/types.ts +177 -0
  41. package/src/generators/nuxt-server/generator.ts +272 -272
  42. package/src/generators/shared/runtime/apiHelpers.ts +535 -507
  43. package/src/generators/shared/runtime/pagination.ts +323 -0
  44. package/src/generators/shared/runtime/useDeleteConnector.ts +109 -0
  45. package/src/generators/shared/runtime/useDetailConnector.ts +64 -0
  46. package/src/generators/shared/runtime/useFormConnector.ts +139 -0
  47. package/src/generators/shared/runtime/useListConnector.ts +148 -0
  48. package/src/generators/shared/runtime/zod-error-merger.ts +119 -0
  49. package/src/generators/shared/templates/api-callbacks-plugin.ts +399 -352
  50. package/src/generators/shared/templates/api-pagination-plugin.ts +158 -0
  51. package/src/generators/use-async-data/generator.ts +205 -205
  52. package/src/generators/use-async-data/runtime/useApiAsyncData.ts +329 -229
  53. package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +324 -245
  54. package/src/generators/use-async-data/templates.ts +257 -257
  55. package/src/generators/use-fetch/generator.ts +170 -170
  56. package/src/generators/use-fetch/runtime/useApiRequest.ts +354 -234
  57. package/src/generators/use-fetch/templates.ts +214 -214
  58. package/src/index.ts +305 -265
  59. package/src/module/index.ts +158 -133
  60. package/src/module/types.ts +39 -31
@@ -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)