nuxt-openapi-hyperfetch 0.1.0-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 -0
- package/.prettierignore +17 -0
- package/.prettierrc.json +12 -0
- package/CONTRIBUTING.md +292 -0
- package/INSTRUCTIONS.md +327 -0
- package/LICENSE +202 -0
- package/README.md +202 -0
- package/dist/cli/config.d.ts +57 -0
- package/dist/cli/config.js +85 -0
- package/dist/cli/logger.d.ts +44 -0
- package/dist/cli/logger.js +58 -0
- package/dist/cli/logo.d.ts +6 -0
- package/dist/cli/logo.js +21 -0
- package/dist/cli/messages.d.ts +65 -0
- package/dist/cli/messages.js +86 -0
- package/dist/cli/prompts.d.ts +30 -0
- package/dist/cli/prompts.js +118 -0
- package/dist/cli/types.d.ts +43 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli/utils.d.ts +26 -0
- package/dist/cli/utils.js +45 -0
- package/dist/generate.d.ts +6 -0
- package/dist/generate.js +48 -0
- package/dist/generators/nuxt-server/bff-templates.d.ts +25 -0
- package/dist/generators/nuxt-server/bff-templates.js +737 -0
- package/dist/generators/nuxt-server/generator.d.ts +7 -0
- package/dist/generators/nuxt-server/generator.js +206 -0
- package/dist/generators/nuxt-server/parser.d.ts +5 -0
- package/dist/generators/nuxt-server/parser.js +5 -0
- package/dist/generators/nuxt-server/templates.d.ts +35 -0
- package/dist/generators/nuxt-server/templates.js +412 -0
- package/dist/generators/nuxt-server/types.d.ts +5 -0
- package/dist/generators/nuxt-server/types.js +5 -0
- package/dist/generators/shared/parsers/heyapi-parser.d.ts +11 -0
- package/dist/generators/shared/parsers/heyapi-parser.js +248 -0
- package/dist/generators/shared/parsers/official-parser.d.ts +5 -0
- package/dist/generators/shared/parsers/official-parser.js +5 -0
- package/dist/generators/shared/runtime/apiHelpers.d.ts +183 -0
- package/dist/generators/shared/runtime/apiHelpers.js +268 -0
- package/dist/generators/shared/templates/api-callbacks-plugin.d.ts +178 -0
- package/dist/generators/shared/templates/api-callbacks-plugin.js +338 -0
- package/dist/generators/shared/types.d.ts +25 -0
- package/dist/generators/shared/types.js +4 -0
- package/dist/generators/tanstack-query/generator.d.ts +5 -0
- package/dist/generators/tanstack-query/generator.js +11 -0
- package/dist/generators/use-async-data/generator.d.ts +5 -0
- package/dist/generators/use-async-data/generator.js +156 -0
- package/dist/generators/use-async-data/parser.d.ts +5 -0
- package/dist/generators/use-async-data/parser.js +5 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncData.d.ts +38 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncData.js +122 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.d.ts +54 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +126 -0
- package/dist/generators/use-async-data/templates.d.ts +20 -0
- package/dist/generators/use-async-data/templates.js +191 -0
- package/dist/generators/use-async-data/types.d.ts +4 -0
- package/dist/generators/use-async-data/types.js +4 -0
- package/dist/generators/use-fetch/generator.d.ts +5 -0
- package/dist/generators/use-fetch/generator.js +131 -0
- package/dist/generators/use-fetch/parser.d.ts +9 -0
- package/dist/generators/use-fetch/parser.js +282 -0
- package/dist/generators/use-fetch/runtime/useApiRequest.d.ts +46 -0
- package/dist/generators/use-fetch/runtime/useApiRequest.js +158 -0
- package/dist/generators/use-fetch/templates.d.ts +16 -0
- package/dist/generators/use-fetch/templates.js +169 -0
- package/dist/generators/use-fetch/types.d.ts +5 -0
- package/dist/generators/use-fetch/types.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +213 -0
- package/docs/API-REFERENCE.md +887 -0
- package/docs/ARCHITECTURE.md +649 -0
- package/docs/DEVELOPMENT.md +918 -0
- package/docs/QUICK-START.md +323 -0
- package/docs/README.md +155 -0
- package/docs/TROUBLESHOOTING.md +881 -0
- package/eslint.config.js +72 -0
- package/package.json +65 -0
- package/src/cli/config.ts +140 -0
- package/src/cli/logger.ts +66 -0
- package/src/cli/logo.ts +25 -0
- package/src/cli/messages.ts +97 -0
- package/src/cli/prompts.ts +143 -0
- package/src/cli/types.ts +50 -0
- package/src/cli/utils.ts +49 -0
- package/src/generate.ts +57 -0
- package/src/generators/nuxt-server/bff-templates.ts +754 -0
- package/src/generators/nuxt-server/generator.ts +270 -0
- package/src/generators/nuxt-server/parser.ts +5 -0
- package/src/generators/nuxt-server/templates.ts +483 -0
- package/src/generators/nuxt-server/types.ts +5 -0
- package/src/generators/shared/parsers/heyapi-parser.ts +307 -0
- package/src/generators/shared/parsers/official-parser.ts +5 -0
- package/src/generators/shared/runtime/apiHelpers.ts +466 -0
- package/src/generators/shared/templates/api-callbacks-plugin.ts +352 -0
- package/src/generators/shared/types.ts +27 -0
- package/src/generators/tanstack-query/generator.ts +11 -0
- package/src/generators/use-async-data/generator.ts +204 -0
- package/src/generators/use-async-data/parser.ts +5 -0
- package/src/generators/use-async-data/runtime/useApiAsyncData.ts +220 -0
- package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +236 -0
- package/src/generators/use-async-data/templates.ts +250 -0
- package/src/generators/use-async-data/types.ts +4 -0
- package/src/generators/use-fetch/generator.ts +169 -0
- package/src/generators/use-fetch/parser.ts +341 -0
- package/src/generators/use-fetch/runtime/useApiRequest.ts +223 -0
- package/src/generators/use-fetch/templates.ts +214 -0
- package/src/generators/use-fetch/types.ts +5 -0
- package/src/index.ts +265 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import type { MethodInfo } from './types.js';
|
|
2
|
+
import { camelCase, pascalCase } from 'change-case';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate file header with auto-generation warning
|
|
6
|
+
*/
|
|
7
|
+
function generateFileHeader(): string {
|
|
8
|
+
return `/**
|
|
9
|
+
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
10
|
+
*
|
|
11
|
+
* This file was automatically generated by nuxt-openapi-generator.
|
|
12
|
+
* Any manual changes will be overwritten on the next generation.
|
|
13
|
+
*
|
|
14
|
+
* @generated by nuxt-openapi-generator
|
|
15
|
+
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/* eslint-disable */
|
|
19
|
+
// @ts-nocheck
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate a complete server route file
|
|
25
|
+
*/
|
|
26
|
+
export function generateServerRouteFile(
|
|
27
|
+
method: MethodInfo,
|
|
28
|
+
apiImportPath: string,
|
|
29
|
+
options?: {
|
|
30
|
+
enableBff?: boolean;
|
|
31
|
+
resource?: string;
|
|
32
|
+
}
|
|
33
|
+
): string {
|
|
34
|
+
const header = generateFileHeader();
|
|
35
|
+
const imports = generateImports(method, apiImportPath);
|
|
36
|
+
const handlerBody = generateHandlerBody(method, options);
|
|
37
|
+
|
|
38
|
+
return `${header}${imports}\n\n${handlerBody}\n`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Calculate the file path for a server route
|
|
43
|
+
* Examples:
|
|
44
|
+
* /pet + GET -> pet/index.get.ts
|
|
45
|
+
* /pet + POST -> pet/index.post.ts
|
|
46
|
+
* /pet/{petId} + GET -> pet/[id].get.ts
|
|
47
|
+
* /pet/{petId}/uploadImage + POST -> pet/[id]/uploadImage.post.ts
|
|
48
|
+
* /store/inventory + GET -> store/inventory.get.ts
|
|
49
|
+
* /user/{username} + GET -> user/[username].get.ts
|
|
50
|
+
*/
|
|
51
|
+
export function generateRouteFilePath(method: MethodInfo): string {
|
|
52
|
+
let routePath = method.path;
|
|
53
|
+
|
|
54
|
+
// Remove leading slash
|
|
55
|
+
if (routePath.startsWith('/')) {
|
|
56
|
+
routePath = routePath.substring(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Replace path params: {petId} -> [id], {username} -> [username]
|
|
60
|
+
routePath = routePath.replace(/\{(\w+)\}/g, (match, paramName) => {
|
|
61
|
+
// Simplify common params
|
|
62
|
+
const simplified = paramName.toLowerCase().replace(/id$/, '');
|
|
63
|
+
return `[${simplified || 'id'}]`;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Split path into segments
|
|
67
|
+
const segments = routePath.split('/').filter(Boolean);
|
|
68
|
+
|
|
69
|
+
// If empty or root, use 'index'
|
|
70
|
+
if (segments.length === 0) {
|
|
71
|
+
return `index.${method.httpMethod.toLowerCase()}.ts`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check if last segment is a dynamic param [xxx]
|
|
75
|
+
const lastSegment = segments[segments.length - 1];
|
|
76
|
+
const isDynamicParam = lastSegment.startsWith('[') && lastSegment.endsWith(']');
|
|
77
|
+
|
|
78
|
+
// If last segment is dynamic, add index
|
|
79
|
+
if (isDynamicParam) {
|
|
80
|
+
return `${segments.join('/')}/index.${method.httpMethod.toLowerCase()}.ts`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Otherwise, use the last segment as filename
|
|
84
|
+
const fileName = segments.pop();
|
|
85
|
+
const dir = segments.length > 0 ? segments.join('/') + '/' : '';
|
|
86
|
+
|
|
87
|
+
return `${dir}${fileName}.${method.httpMethod.toLowerCase()}.ts`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Extract base type names from a type string (same as use-fetch)
|
|
92
|
+
*/
|
|
93
|
+
function extractBaseTypes(type: string): string[] {
|
|
94
|
+
if (!type) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Handle array syntax: Pet[]
|
|
99
|
+
const arrayMatch = type.match(/^(\w+)\[\]$/);
|
|
100
|
+
if (arrayMatch) {
|
|
101
|
+
return [arrayMatch[1]];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Handle Array generic: Array<Pet>
|
|
105
|
+
const arrayGenericMatch = type.match(/^Array<(\w+)>$/);
|
|
106
|
+
if (arrayGenericMatch) {
|
|
107
|
+
return [arrayGenericMatch[1]];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// If it's a simple named type (single word, PascalCase), include it
|
|
111
|
+
if (/^[A-Z][a-zA-Z0-9]*$/.test(type)) {
|
|
112
|
+
return [type];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Generate import statements
|
|
120
|
+
*/
|
|
121
|
+
function generateImports(method: MethodInfo, apiImportPath: string): string {
|
|
122
|
+
const h3Imports: string[] = ['defineEventHandler', 'createError'];
|
|
123
|
+
|
|
124
|
+
// Add h3 imports based on method needs
|
|
125
|
+
if (method.pathParams.length > 0) {
|
|
126
|
+
h3Imports.push('getRouterParam');
|
|
127
|
+
}
|
|
128
|
+
if (method.hasQueryParams) {
|
|
129
|
+
h3Imports.push('getQuery');
|
|
130
|
+
}
|
|
131
|
+
if (method.hasBody) {
|
|
132
|
+
h3Imports.push('readBody');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let imports = `import { ${h3Imports.join(', ')} } from 'h3'\n`;
|
|
136
|
+
|
|
137
|
+
// Import types
|
|
138
|
+
const typeNames = new Set<string>();
|
|
139
|
+
|
|
140
|
+
// Extract base types from request type
|
|
141
|
+
if (method.requestType) {
|
|
142
|
+
const extracted = extractBaseTypes(method.requestType);
|
|
143
|
+
extracted.forEach((t) => typeNames.add(t));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Extract base types from response type
|
|
147
|
+
if (method.responseType && method.responseType !== 'void') {
|
|
148
|
+
const extracted = extractBaseTypes(method.responseType);
|
|
149
|
+
extracted.forEach((t) => typeNames.add(t));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Import types from API (only if we have named types to import)
|
|
153
|
+
if (typeNames.size > 0) {
|
|
154
|
+
imports += `import type { ${Array.from(typeNames).join(', ')} } from '${apiImportPath}'\n`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return imports;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Generate the handler body
|
|
162
|
+
*/
|
|
163
|
+
function generateHandlerBody(
|
|
164
|
+
method: MethodInfo,
|
|
165
|
+
options?: {
|
|
166
|
+
enableBff?: boolean;
|
|
167
|
+
resource?: string;
|
|
168
|
+
}
|
|
169
|
+
): string {
|
|
170
|
+
const description = method.description ? `/**\n * ${method.description}\n */\n` : '';
|
|
171
|
+
|
|
172
|
+
const pathParamCapture = generatePathParamCapture(method);
|
|
173
|
+
const queryCapture = generateQueryCapture(method);
|
|
174
|
+
const bodyCapture = generateBodyCapture(method);
|
|
175
|
+
const backendUrl = generateBackendUrl(method);
|
|
176
|
+
const fetchOptions = generateFetchOptions(method);
|
|
177
|
+
|
|
178
|
+
// BFF: Auth context loading
|
|
179
|
+
const authContextCode = options?.enableBff
|
|
180
|
+
? ` // Try to load auth context (optional)
|
|
181
|
+
let auth = null
|
|
182
|
+
try {
|
|
183
|
+
const { getAuthContext } = await import('~/server/auth/context')
|
|
184
|
+
auth = await getAuthContext(event)
|
|
185
|
+
} catch {
|
|
186
|
+
// Auth not configured - continue without it
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
`
|
|
190
|
+
: '';
|
|
191
|
+
|
|
192
|
+
// BFF: Transformer call
|
|
193
|
+
const transformerCode =
|
|
194
|
+
options?.enableBff && options?.resource
|
|
195
|
+
? `
|
|
196
|
+
// Try to transform data (optional)
|
|
197
|
+
try {
|
|
198
|
+
const { transform${pascalCase(options.resource)} } = await import('~/server/bff/transformers/${options.resource}')
|
|
199
|
+
return await transform${pascalCase(options.resource)}(data, event, auth)
|
|
200
|
+
} catch {
|
|
201
|
+
// Transformer not found - return raw data
|
|
202
|
+
return data
|
|
203
|
+
}`
|
|
204
|
+
: `
|
|
205
|
+
return data`;
|
|
206
|
+
|
|
207
|
+
return `${description}export default defineEventHandler(async (event): Promise<${method.responseType}> => {
|
|
208
|
+
${pathParamCapture}${queryCapture}${bodyCapture}${authContextCode}const config = useRuntimeConfig()
|
|
209
|
+
const baseUrl = config.apiBaseUrl
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const data = await $fetch<${method.responseType}>(${backendUrl}, {
|
|
213
|
+
${fetchOptions}
|
|
214
|
+
})${transformerCode}
|
|
215
|
+
} catch (error: any) {
|
|
216
|
+
throw createError({
|
|
217
|
+
statusCode: error.statusCode || 500,
|
|
218
|
+
statusMessage: error.message || 'Request failed'
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
})`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Generate path param capture code
|
|
226
|
+
*/
|
|
227
|
+
function generatePathParamCapture(method: MethodInfo): string {
|
|
228
|
+
if (method.pathParams.length === 0) {
|
|
229
|
+
return '';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const captures = method.pathParams.map((param) => {
|
|
233
|
+
const paramName = camelCase(param);
|
|
234
|
+
const paramKey = param.toLowerCase().replace(/id$/, '') || 'id';
|
|
235
|
+
|
|
236
|
+
return `const ${paramName} = getRouterParam(event, '${paramKey}')
|
|
237
|
+
if (!${paramName}) {
|
|
238
|
+
throw createError({
|
|
239
|
+
statusCode: 400,
|
|
240
|
+
statusMessage: '${param} is required'
|
|
241
|
+
})
|
|
242
|
+
}
|
|
243
|
+
`;
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return captures.join('') + '\n ';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Generate query param capture code
|
|
251
|
+
*/
|
|
252
|
+
function generateQueryCapture(method: MethodInfo): string {
|
|
253
|
+
if (!method.hasQueryParams) {
|
|
254
|
+
return '';
|
|
255
|
+
}
|
|
256
|
+
return 'const query = getQuery(event)\n ';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Generate body capture code
|
|
261
|
+
*/
|
|
262
|
+
function generateBodyCapture(method: MethodInfo): string {
|
|
263
|
+
if (!method.hasBody) {
|
|
264
|
+
return '';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const typeAnnotation = method.requestType ? `<${method.requestType}>` : '';
|
|
268
|
+
return `const body = await readBody${typeAnnotation}(event)\n `;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Generate backend URL with path params replaced
|
|
273
|
+
*/
|
|
274
|
+
function generateBackendUrl(method: MethodInfo): string {
|
|
275
|
+
let url = method.path;
|
|
276
|
+
|
|
277
|
+
// Replace {param} with ${paramName}
|
|
278
|
+
if (method.pathParams.length > 0) {
|
|
279
|
+
for (const param of method.pathParams) {
|
|
280
|
+
const paramName = camelCase(param);
|
|
281
|
+
url = url.replace(`{${param}}`, `\${${paramName}}`);
|
|
282
|
+
}
|
|
283
|
+
return `\`\${baseUrl}${url}\``;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Static URL
|
|
287
|
+
return `\`\${baseUrl}${url}\``;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Generate $fetch options object
|
|
292
|
+
*/
|
|
293
|
+
function generateFetchOptions(method: MethodInfo): string {
|
|
294
|
+
const options: string[] = [];
|
|
295
|
+
|
|
296
|
+
// Method (if not GET)
|
|
297
|
+
if (method.httpMethod !== 'GET') {
|
|
298
|
+
options.push(`method: '${method.httpMethod}'`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Query params
|
|
302
|
+
if (method.hasQueryParams) {
|
|
303
|
+
options.push('query: query');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Body
|
|
307
|
+
if (method.hasBody) {
|
|
308
|
+
options.push('body: body');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Headers
|
|
312
|
+
const headerLines: string[] = [];
|
|
313
|
+
if (method.hasBody) {
|
|
314
|
+
headerLines.push(`'Content-Type': 'application/json'`);
|
|
315
|
+
}
|
|
316
|
+
headerLines.push(
|
|
317
|
+
`...(config.apiSecret ? { 'Authorization': \`Bearer \${config.apiSecret}\` } : {})`
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
options.push(`headers: {\n ${headerLines.join(',\n ')}\n }`);
|
|
321
|
+
|
|
322
|
+
return options.join(',\n ');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Generate nuxt.config.example.ts
|
|
327
|
+
*/
|
|
328
|
+
export function generateConfigFile(): string {
|
|
329
|
+
return `/**
|
|
330
|
+
* ⚠️ AUTO-GENERATED EXAMPLE FILE
|
|
331
|
+
*
|
|
332
|
+
* Copy this configuration to your nuxt.config.ts
|
|
333
|
+
* @generated by nuxt-openapi-generator
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
// nuxt.config.ts
|
|
337
|
+
export default defineNuxtConfig({
|
|
338
|
+
runtimeConfig: {
|
|
339
|
+
// Private keys (server-only, never exposed to client)
|
|
340
|
+
apiSecret: process.env.API_SECRET || '',
|
|
341
|
+
apiBaseUrl: process.env.API_BASE_URL || 'https://petstore3.swagger.io/api/v3'
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
`;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Generate .env.example
|
|
349
|
+
*/
|
|
350
|
+
export function generateEnvFile(): string {
|
|
351
|
+
return `# ⚠️ AUTO-GENERATED EXAMPLE FILE
|
|
352
|
+
# Copy this file to .env and configure your values
|
|
353
|
+
# @generated by nuxt-openapi-generator
|
|
354
|
+
|
|
355
|
+
# Backend API Configuration
|
|
356
|
+
API_BASE_URL=https://petstore3.swagger.io/api/v3
|
|
357
|
+
API_SECRET=your-api-secret-token-here
|
|
358
|
+
`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Generate README.md
|
|
363
|
+
*/
|
|
364
|
+
export function generateReadme(serverPath: string): string {
|
|
365
|
+
return `<!--
|
|
366
|
+
⚠️ AUTO-GENERATED DOCUMENTATION
|
|
367
|
+
This file was automatically generated by nuxt-openapi-generator.
|
|
368
|
+
@generated by nuxt-openapi-generator
|
|
369
|
+
-->
|
|
370
|
+
|
|
371
|
+
# Nuxt Server Routes
|
|
372
|
+
|
|
373
|
+
Auto-generated server routes that proxy requests to your backend API.
|
|
374
|
+
|
|
375
|
+
## 🔧 Configuration
|
|
376
|
+
|
|
377
|
+
1. **Copy \`.env.example\` to \`.env\`**:
|
|
378
|
+
\`\`\`bash
|
|
379
|
+
cp .env.example .env
|
|
380
|
+
\`\`\`
|
|
381
|
+
|
|
382
|
+
2. **Update \`.env\` with your backend URL**:
|
|
383
|
+
\`\`\`env
|
|
384
|
+
API_BASE_URL=https://your-backend-api.com/api/v3
|
|
385
|
+
API_SECRET=your-secret-token
|
|
386
|
+
\`\`\`
|
|
387
|
+
|
|
388
|
+
3. **Update \`nuxt.config.ts\`** (see \`nuxt.config.example.ts\`):
|
|
389
|
+
\`\`\`typescript
|
|
390
|
+
export default defineNuxtConfig({
|
|
391
|
+
runtimeConfig: {
|
|
392
|
+
// All these variables are server-only
|
|
393
|
+
apiSecret: process.env.API_SECRET || '',
|
|
394
|
+
apiBaseUrl: process.env.API_BASE_URL || ''
|
|
395
|
+
}
|
|
396
|
+
})
|
|
397
|
+
\`\`\`
|
|
398
|
+
|
|
399
|
+
## 🚀 Usage
|
|
400
|
+
|
|
401
|
+
All routes are available at \`/api/*\`:
|
|
402
|
+
|
|
403
|
+
\`\`\`typescript
|
|
404
|
+
// In your Vue components
|
|
405
|
+
const { data: pet } = await useFetch('/api/pet/123')
|
|
406
|
+
|
|
407
|
+
// With query params
|
|
408
|
+
const { data: pets } = await useFetch('/api/pet', {
|
|
409
|
+
query: { status: 'available' }
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
// POST request
|
|
413
|
+
const { data: newPet } = await useFetch('/api/pet', {
|
|
414
|
+
method: 'POST',
|
|
415
|
+
body: { name: 'Fluffy', status: 'available' }
|
|
416
|
+
})
|
|
417
|
+
\`\`\`
|
|
418
|
+
|
|
419
|
+
## 📁 Generated Routes
|
|
420
|
+
|
|
421
|
+
- **${serverPath}/** - All server routes
|
|
422
|
+
- Each file corresponds to an OpenAPI endpoint
|
|
423
|
+
- Path params use dynamic routes: \`[id]\`, \`[username]\`
|
|
424
|
+
- HTTP methods: \`.get.ts\`, \`.post.ts\`, \`.put.ts\`, \`.delete.ts\`
|
|
425
|
+
|
|
426
|
+
## 🔒 Security
|
|
427
|
+
|
|
428
|
+
- \`API_SECRET\` is only available server-side (never exposed to client)
|
|
429
|
+
- All requests go through your Nuxt server (CORS handled automatically)
|
|
430
|
+
- You can add custom authentication/validation logic in each route
|
|
431
|
+
|
|
432
|
+
## 🛠️ Customization
|
|
433
|
+
|
|
434
|
+
Each generated route can be customized:
|
|
435
|
+
|
|
436
|
+
\`\`\`typescript
|
|
437
|
+
// server/api/pet/[id].get.ts
|
|
438
|
+
export default defineEventHandler(async (event) => {
|
|
439
|
+
const petId = getRouterParam(event, 'id')
|
|
440
|
+
|
|
441
|
+
// Add custom logic here
|
|
442
|
+
// - Rate limiting
|
|
443
|
+
// - Caching
|
|
444
|
+
// - Request validation
|
|
445
|
+
// - Response transformation
|
|
446
|
+
|
|
447
|
+
const config = useRuntimeConfig()
|
|
448
|
+
const data = await \$fetch(\`\${config.apiBaseUrl}/pet/\${petId}\`)
|
|
449
|
+
|
|
450
|
+
return data
|
|
451
|
+
})
|
|
452
|
+
\`\`\`
|
|
453
|
+
`;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Generate index file that exports all route paths (optional, for documentation)
|
|
458
|
+
*/
|
|
459
|
+
export function generateRoutesIndexFile(methods: MethodInfo[]): string {
|
|
460
|
+
const header = generateFileHeader();
|
|
461
|
+
const routes = methods.map((method) => {
|
|
462
|
+
const filePath = generateRouteFilePath(method);
|
|
463
|
+
const routePath =
|
|
464
|
+
'/' +
|
|
465
|
+
filePath
|
|
466
|
+
.replace(/\\/g, '/')
|
|
467
|
+
.replace(/index\.(get|post|put|delete|patch)\.ts$/, '')
|
|
468
|
+
.replace(/\.(get|post|put|delete|patch)\.ts$/, '')
|
|
469
|
+
.replace(/\[(\w+)\]/g, ':$1');
|
|
470
|
+
|
|
471
|
+
return ` // ${method.httpMethod} ${routePath} -> ${filePath}`;
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
return `${header}/**
|
|
475
|
+
* Generated Server Routes
|
|
476
|
+
*
|
|
477
|
+
* Available routes:
|
|
478
|
+
${routes.join('\n')}
|
|
479
|
+
*/
|
|
480
|
+
|
|
481
|
+
export default {}
|
|
482
|
+
`;
|
|
483
|
+
}
|