next-openapi-gen 0.3.3 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -151
- package/dist/components/scalar.js +16 -16
- package/dist/lib/openapi-generator.js +40 -1
- package/dist/lib/route-processor.js +23 -3
- package/dist/lib/schema-processor.js +88 -17
- package/dist/lib/utils.js +12 -12
- package/dist/lib/zod-converter.js +916 -0
- package/dist/openapi-template.js +1 -0
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
# next-openapi-gen
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Automatically generate OpenAPI 3.0 documentation from Next.js projects, with support for both TypeScript types and Zod schemas.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
- Next.js
|
|
8
|
-
-
|
|
7
|
+
- ✅ Automatic OpenAPI documentation generation from Next.js code
|
|
8
|
+
- ✅ Support for Next.js App Router (including `/api/users/[id]/route.ts` routes)
|
|
9
|
+
- ✅ TypeScript types support
|
|
10
|
+
- ✅ Zod schemas support
|
|
11
|
+
- ✅ JSDoc comments support
|
|
12
|
+
- ✅ Multiple UI interfaces: `Scalar`, `Swagger`, `Redoc`, `Stoplight` and `Rapidoc` available at `/api-docs` url.
|
|
13
|
+
- ✅ Path parameters detection (`/users/{id}`)
|
|
14
|
+
- ✅ Intelligent parameter examples
|
|
15
|
+
- ✅ Intuitive CLI for initialization and documentation generation
|
|
9
16
|
|
|
10
|
-
##
|
|
17
|
+
## Supported interfaces
|
|
11
18
|
|
|
12
19
|
- Scalar 🆕
|
|
13
20
|
- Swagger
|
|
@@ -15,182 +22,350 @@
|
|
|
15
22
|
- Stoplight Elements
|
|
16
23
|
- RapiDoc
|
|
17
24
|
|
|
18
|
-
## Features
|
|
19
|
-
|
|
20
|
-
- **Automatic OpenAPI Generation**: Generate OpenAPI 3.0 documentation from your Next.js routes, automatically parsing TypeScript types for parameters, request bodies and responses. Field comments in TypeScript types are reflected as descriptions in the OpenAPI schema.
|
|
21
|
-
- **Complex TypeScript Types**: Use complex TypeScript types, such as `nested objects`, `arrays`, `enums` and `unions` (mapped to anyOf). This enables a more comprehensive representation of data structures directly in the OpenAPI schema.
|
|
22
|
-
- **JSDoc-Based Documentation**: Document API routes with optional JSDoc comments, including tags like `@openapi`, `@auth`, `@desc`, `@params`, `@body`, and `@response` to easily define route metadata.
|
|
23
|
-
- **Multiple UI Interfaces**: Choose between `Scalar`, `Swagger UI`, `Redoc`, `Stoplight Elements` or `RapiDoc` to visualize your API documentation. Customize the interface to fit your preferences.
|
|
24
|
-
- **Real-time Documentation**: As your API evolves, regenerate the OpenAPI documentation with a single command, ensuring your documentation is always up to date.
|
|
25
|
-
- **Easy configuration**: Customize generator behavior using the `next.openapi.json` configuration file, allowing for quick adjustments without modifying the code.
|
|
26
|
-
|
|
27
25
|
## Installation
|
|
28
26
|
|
|
29
27
|
```bash
|
|
30
|
-
|
|
28
|
+
npm install next-openapi-gen --save-dev
|
|
31
29
|
```
|
|
32
30
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
### Step 1: Initialize Configuration and Setup
|
|
36
|
-
|
|
37
|
-
Run the following command to generate the `next.openapi.json` configuration file and automatically set up Scalar UI with `/api-docs` routes:
|
|
31
|
+
## Quick Start
|
|
38
32
|
|
|
39
33
|
```bash
|
|
34
|
+
# Initialize OpenAPI configuration
|
|
40
35
|
npx next-openapi-gen init --ui scalar --docs-url api-docs
|
|
36
|
+
|
|
37
|
+
# Generate OpenAPI documentation
|
|
38
|
+
npx next-openapi-gen generate
|
|
41
39
|
```
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
During initialization (`npx next-openapi-gen init`), a configuration file `next.openapi.json` will be created in the project's root directory:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"openapi": "3.0.0",
|
|
48
|
+
"info": {
|
|
49
|
+
"title": "Next.js API",
|
|
50
|
+
"version": "1.0.0",
|
|
51
|
+
"description": "API generated by next-openapi-gen"
|
|
52
|
+
},
|
|
53
|
+
"servers": [
|
|
54
|
+
{
|
|
55
|
+
"url": "http://localhost:3000",
|
|
56
|
+
"description": "Local server"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"apiDir": "src/app/api",
|
|
60
|
+
"schemaDir": "src/types",
|
|
61
|
+
"schemaType": "typescript", // or "zod" for Zod schemas
|
|
62
|
+
"outputFile": "openapi.json",
|
|
63
|
+
"docsUrl": "/api-docs",
|
|
64
|
+
"includeOpenApiRoutes": false
|
|
65
|
+
}
|
|
66
|
+
```
|
|
46
67
|
|
|
47
|
-
|
|
68
|
+
### Configuration Options
|
|
69
|
+
|
|
70
|
+
| Option | Description |
|
|
71
|
+
|-------|------|
|
|
72
|
+
| `apiDir` | Path to the API directory |
|
|
73
|
+
| `schemaDir` | Path to the types/schemas directory |
|
|
74
|
+
| `schemaType` | Schema type: `"typescript"` or `"zod"` |
|
|
75
|
+
| `outputFile` | Path to the OpenAPI output file |
|
|
76
|
+
| `docsUrl` | API documentation URL (for Swagger UI) |
|
|
77
|
+
| `includeOpenApiRoutes` | Whether to include only routes with @openapi tag |
|
|
78
|
+
|
|
79
|
+
## Documenting Your API
|
|
80
|
+
|
|
81
|
+
### With TypeScript Types
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// src/app/api/users/[id]/route.ts
|
|
85
|
+
|
|
86
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
87
|
+
|
|
88
|
+
type UserParams = {
|
|
89
|
+
id: string; // User ID
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
type UserResponse = {
|
|
93
|
+
id: string; // User ID
|
|
94
|
+
name: string; // Full name
|
|
95
|
+
email: string; // Email address
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get user information
|
|
100
|
+
* @desc Fetches detailed user information by ID
|
|
101
|
+
* @pathParams UserParams
|
|
102
|
+
* @response UserResponse
|
|
103
|
+
* @openapi
|
|
104
|
+
*/
|
|
105
|
+
export async function GET(
|
|
106
|
+
request: NextRequest,
|
|
107
|
+
{ params }: { params: { id: string } }
|
|
108
|
+
) {
|
|
109
|
+
// Implementation...
|
|
110
|
+
}
|
|
111
|
+
```
|
|
48
112
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
113
|
+
### With Zod Schemas
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// src/app/api/products/[id]/route.ts
|
|
117
|
+
|
|
118
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
119
|
+
import { z } from 'zod';
|
|
120
|
+
|
|
121
|
+
export const ProductParams = z.object({
|
|
122
|
+
id: z.string().describe("Product ID")
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export const ProductResponse = z.object({
|
|
126
|
+
id: z.string().describe("Product ID"),
|
|
127
|
+
name: z.string().describe("Product name"),
|
|
128
|
+
price: z.number().positive().describe("Product price")
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get product information
|
|
133
|
+
* @desc Fetches detailed product information by ID
|
|
134
|
+
* @pathParams ProductParams
|
|
135
|
+
* @response ProductResponse
|
|
136
|
+
* @openapi
|
|
137
|
+
*/
|
|
138
|
+
export async function GET(
|
|
139
|
+
request: NextRequest,
|
|
140
|
+
{ params }: { params: { id: string } }
|
|
141
|
+
) {
|
|
142
|
+
// Implementation...
|
|
143
|
+
}
|
|
144
|
+
```
|
|
52
145
|
|
|
53
|
-
|
|
146
|
+
## JSDoc Documentation Tags
|
|
54
147
|
|
|
55
|
-
|
|
148
|
+
| Tag | Description |
|
|
149
|
+
|-----|------|
|
|
150
|
+
| `@desc` | Endpoint description |
|
|
151
|
+
| `@pathParams` | Path parameters type/schema |
|
|
152
|
+
| `@params` | Query parameters type/schema |
|
|
153
|
+
| `@body` | Request body type/schema |
|
|
154
|
+
| `@response` | Response type/schema |
|
|
155
|
+
| `@auth` | Authorization type (`bearer`, `basic`, `apikey`) |
|
|
156
|
+
| `@openapi` | Marks the route for inclusion in documentation (if includeOpenApiRoutes is enabled) |
|
|
56
157
|
|
|
57
|
-
|
|
58
|
-
<table>
|
|
59
|
-
<tr>
|
|
60
|
-
<th>Login route</th>
|
|
61
|
-
<th>Scalar / Swagger</th>
|
|
62
|
-
</tr>
|
|
63
|
-
<tr>
|
|
64
|
-
<td>
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
//app/api/auth/login/route.ts
|
|
68
|
-
|
|
69
|
-
type LoginBody = {
|
|
70
|
-
email: string; // user email
|
|
71
|
-
password: string; // user password
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
type LoginResponse = {
|
|
75
|
-
token: string; // auth token
|
|
76
|
-
refresh_token: string; // refresh token
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Authenticate as a user.
|
|
81
|
-
* @desc: Login a user
|
|
82
|
-
* @body: LoginBody
|
|
83
|
-
* @response: LoginResponse
|
|
84
|
-
*/
|
|
85
|
-
export async function POST(req: Request) {
|
|
86
|
-
...
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
</td>
|
|
90
|
-
<td>
|
|
91
|
-
<img width="340" alt="api-login-scalar" src="https://raw.githubusercontent.com/tazo90/next-openapi-gen/refs/heads/main/assets/api-login-scalar.png" alt-text="api-login"/>
|
|
92
|
-
<br/><br/><br/>
|
|
93
|
-
<img width="340" alt="api-login-swagger" src="https://raw.githubusercontent.com/tazo90/next-openapi-gen/refs/heads/main/assets/api-login-swagger.png" alt-text="api-login-swagger"/>
|
|
94
|
-
</td>
|
|
95
|
-
</tr>
|
|
96
|
-
|
|
97
|
-
<tr>
|
|
98
|
-
<th>Users route</th>
|
|
99
|
-
<th>Scalar / Swagger</th>
|
|
100
|
-
</tr>
|
|
101
|
-
<tr>
|
|
102
|
-
<td>
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
//app/api/users/route.ts
|
|
106
|
-
|
|
107
|
-
enum ROLE {
|
|
108
|
-
OWNER,
|
|
109
|
-
MEMBER,
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
type User = {
|
|
113
|
-
id: number;
|
|
114
|
-
name: string;
|
|
115
|
-
email: string;
|
|
116
|
-
role: ROLE;
|
|
117
|
-
address: Address;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
type Address = {
|
|
121
|
-
line1: string;
|
|
122
|
-
line2?: string;
|
|
123
|
-
city: string;
|
|
124
|
-
postalCode: string;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
type UsersParams = {
|
|
128
|
-
search: string; // search by
|
|
129
|
-
role?: ROLE; // filter by role
|
|
130
|
-
page?: number; // page number
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
type UsersResponse = {
|
|
134
|
-
page?: number;
|
|
135
|
-
count?: number;
|
|
136
|
-
data: User[];
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* List all users.
|
|
141
|
-
* @auth: bearer
|
|
142
|
-
* @params: UsersParams
|
|
143
|
-
* @response: UsersResponse
|
|
144
|
-
*/
|
|
145
|
-
export async function GET(req: Request) {
|
|
146
|
-
...
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
</td>
|
|
150
|
-
<td>
|
|
151
|
-
<img width="340" alt="api-users-scalar" src="https://raw.githubusercontent.com/tazo90/next-openapi-gen/refs/heads/main/assets/api-users-scalar.png" alt-text="api-users-scalar"/>
|
|
152
|
-
<br/><br/><br/>
|
|
153
|
-
<img width="340" alt="api-users-swagger" src="https://raw.githubusercontent.com/tazo90/next-openapi-gen/refs/heads/main/assets/api-users-swagger.png" alt-text="api-users-swagger"/>
|
|
154
|
-
</td>
|
|
155
|
-
</tr>
|
|
156
|
-
</table>
|
|
157
|
-
</div>
|
|
158
|
+
## CLI Usage
|
|
158
159
|
|
|
159
|
-
###
|
|
160
|
+
### 1. Initialization
|
|
160
161
|
|
|
161
|
-
|
|
162
|
+
```bash
|
|
163
|
+
npx next-openapi-gen init
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This command will generate following elements:
|
|
167
|
+
- Generate `next.openapi.json` configuration file
|
|
168
|
+
- Install UI interface (default `Scalar`)
|
|
169
|
+
- Add `/api-docs` page to display OpenAPI documentation
|
|
170
|
+
|
|
171
|
+
### 2. Generate Documentation
|
|
162
172
|
|
|
163
173
|
```bash
|
|
164
174
|
npx next-openapi-gen generate
|
|
165
175
|
```
|
|
166
176
|
|
|
167
|
-
This command
|
|
177
|
+
This command will generate OpenAPI documentation based on your API code:
|
|
178
|
+
- Scan API directories for routes
|
|
179
|
+
- Analyze types/schemas
|
|
180
|
+
- Generate OpenAPI file (`openapi.json`) in `public` folder
|
|
181
|
+
- Create Swagger/Scalar UI endpoint and page (if enabled)
|
|
182
|
+
|
|
183
|
+
### 3. View API Documentation
|
|
184
|
+
|
|
185
|
+
To see API documenation go to `http://localhost:3000/api-docs`
|
|
186
|
+
|
|
187
|
+
## Examples
|
|
168
188
|
|
|
169
|
-
###
|
|
189
|
+
### Path Parameters
|
|
170
190
|
|
|
171
|
-
|
|
191
|
+
```typescript
|
|
192
|
+
// src/app/api/users/[id]/route.ts
|
|
172
193
|
|
|
173
|
-
|
|
194
|
+
// TypeScript
|
|
195
|
+
type UserParams = {
|
|
196
|
+
id: string; // User ID
|
|
197
|
+
};
|
|
174
198
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
- `@body`: Specifies the TypeScript interface for the request body.
|
|
180
|
-
- `@response`: Specifies the TypeScript interface for the response.
|
|
199
|
+
// Or Zod
|
|
200
|
+
const UserParams = z.object({
|
|
201
|
+
id: z.string().describe("User ID")
|
|
202
|
+
});
|
|
181
203
|
|
|
182
|
-
|
|
204
|
+
/**
|
|
205
|
+
* @pathParams UserParams
|
|
206
|
+
*/
|
|
207
|
+
export async function GET() {
|
|
208
|
+
// ...
|
|
209
|
+
}
|
|
210
|
+
```
|
|
183
211
|
|
|
184
|
-
|
|
212
|
+
### Query Parameters
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// src/app/api/users/route.ts
|
|
216
|
+
|
|
217
|
+
// TypeScript
|
|
218
|
+
type UsersQueryParams = {
|
|
219
|
+
page?: number; // Page number
|
|
220
|
+
limit?: number; // Results per page
|
|
221
|
+
search?: string; // Search phrase
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Or Zod
|
|
225
|
+
const UsersQueryParams = z.object({
|
|
226
|
+
page: z.number().optional().describe("Page number"),
|
|
227
|
+
limit: z.number().optional().describe("Results per page"),
|
|
228
|
+
search: z.string().optional().describe("Search phrase")
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @params UsersQueryParams
|
|
233
|
+
*/
|
|
234
|
+
export async function GET() {
|
|
235
|
+
// ...
|
|
236
|
+
}
|
|
237
|
+
```
|
|
185
238
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
239
|
+
### Request Body
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// src/app/api/users/route.ts
|
|
243
|
+
|
|
244
|
+
// TypeScript
|
|
245
|
+
type CreateUserBody = {
|
|
246
|
+
name: string; // Full name
|
|
247
|
+
email: string; // Email address
|
|
248
|
+
password: string; // Password
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Or Zod
|
|
252
|
+
const CreateUserBody = z.object({
|
|
253
|
+
name: z.string().describe("Full name"),
|
|
254
|
+
email: z.string().email().describe("Email address"),
|
|
255
|
+
password: z.string().min(8).describe("Password")
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @body CreateUserBody
|
|
260
|
+
*/
|
|
261
|
+
export async function POST() {
|
|
262
|
+
// ...
|
|
263
|
+
}
|
|
264
|
+
```
|
|
192
265
|
|
|
193
|
-
|
|
266
|
+
### Response
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
// src/app/api/users/route.ts
|
|
270
|
+
|
|
271
|
+
// TypeScript
|
|
272
|
+
type UserResponse = {
|
|
273
|
+
id: string; // User ID
|
|
274
|
+
name: string; // Full name
|
|
275
|
+
email: string; // Email address
|
|
276
|
+
createdAt: Date; // Creation date
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Or Zod
|
|
280
|
+
const UserResponse = z.object({
|
|
281
|
+
id: z.string().describe("User ID"),
|
|
282
|
+
name: z.string().describe("Full name"),
|
|
283
|
+
email: z.string().email().describe("Email address"),
|
|
284
|
+
createdAt: z.date().describe("Creation date")
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @response UserResponse
|
|
289
|
+
*/
|
|
290
|
+
export async function GET() {
|
|
291
|
+
// ...
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Authorization
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// src/app/api/protected/route.ts
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @auth bearer
|
|
302
|
+
*/
|
|
303
|
+
export async function GET() {
|
|
304
|
+
// ...
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Advanced Usage
|
|
309
|
+
|
|
310
|
+
### Automatic Path Parameter Detection
|
|
311
|
+
|
|
312
|
+
The library automatically detects path parameters and generates documentation for them:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// src/app/api/users/[id]/posts/[postId]/route.ts
|
|
316
|
+
|
|
317
|
+
// Will automatically detect 'id' and 'postId' parameters
|
|
318
|
+
export async function GET() {
|
|
319
|
+
// ...
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
If no type/schema is provided for path parameters, a default schema will be generated.
|
|
324
|
+
|
|
325
|
+
### Intelligent Examples
|
|
326
|
+
|
|
327
|
+
The library generates intelligent examples for parameters based on their name:
|
|
328
|
+
|
|
329
|
+
| Parameter name | Example |
|
|
330
|
+
|----------------|----------|
|
|
331
|
+
| `id`, `*Id` | `"123"` or `123` |
|
|
332
|
+
| `slug` | `"example-slug"` |
|
|
333
|
+
| `uuid` | `"123e4567-e89b-12d3-a456-426614174000"` |
|
|
334
|
+
| `email` | `"user@example.com"` |
|
|
335
|
+
| `name` | `"example-name"` |
|
|
336
|
+
| `date` | `"2023-01-01"` |
|
|
337
|
+
|
|
338
|
+
## Advanced Zod Features
|
|
339
|
+
|
|
340
|
+
The library supports advanced Zod features such as:
|
|
341
|
+
|
|
342
|
+
### Validation Chains
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Zod validation chains are properly converted to OpenAPI schemas
|
|
346
|
+
const EmailSchema = z.string().email().min(5).max(100).describe("Email address");
|
|
347
|
+
|
|
348
|
+
// Converts to OpenAPI with email format, minLength and maxLength
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Type Aliases with z.infer
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
// You can use TypeScript with Zod types
|
|
355
|
+
import { z } from 'zod';
|
|
356
|
+
|
|
357
|
+
const UserSchema = z.object({
|
|
358
|
+
id: z.string().uuid(),
|
|
359
|
+
name: z.string().min(2)
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Use z.infer to create a TypeScript type
|
|
363
|
+
type User = z.infer<typeof UserSchema>;
|
|
364
|
+
|
|
365
|
+
// The library will be able to recognize this schema by reference
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Available UI providers
|
|
194
369
|
|
|
195
370
|
<div align="center">
|
|
196
371
|
<table>
|
|
@@ -221,3 +396,8 @@ The `next.openapi.json` file allows you to configure the behavior of the OpenAPI
|
|
|
221
396
|
</tr>
|
|
222
397
|
</tbody>
|
|
223
398
|
</table>
|
|
399
|
+
</div>
|
|
400
|
+
|
|
401
|
+
## License
|
|
402
|
+
|
|
403
|
+
MIT
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
export const scalarDeps = ["@scalar/api-reference-react", "ajv"];
|
|
2
2
|
export function ScalarUI(outputFile) {
|
|
3
|
-
return `
|
|
4
|
-
"use client";
|
|
5
|
-
|
|
6
|
-
import { ApiReferenceReact } from "@scalar/api-reference-react";
|
|
7
|
-
|
|
8
|
-
import "@scalar/api-reference-react/style.css";
|
|
9
|
-
|
|
10
|
-
export default function ApiDocsPage() {
|
|
11
|
-
return (
|
|
12
|
-
<ApiReferenceReact
|
|
13
|
-
configuration={{
|
|
14
|
-
url: "/${outputFile}",
|
|
15
|
-
}}
|
|
16
|
-
/>
|
|
17
|
-
);
|
|
18
|
-
}
|
|
3
|
+
return `
|
|
4
|
+
"use client";
|
|
5
|
+
|
|
6
|
+
import { ApiReferenceReact } from "@scalar/api-reference-react";
|
|
7
|
+
|
|
8
|
+
import "@scalar/api-reference-react/style.css";
|
|
9
|
+
|
|
10
|
+
export default function ApiDocsPage() {
|
|
11
|
+
return (
|
|
12
|
+
<ApiReferenceReact
|
|
13
|
+
configuration={{
|
|
14
|
+
url: "/${outputFile}",
|
|
15
|
+
}}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
19
|
`;
|
|
20
20
|
}
|
|
@@ -14,7 +14,7 @@ export class OpenApiGenerator {
|
|
|
14
14
|
}
|
|
15
15
|
getConfig() {
|
|
16
16
|
// @ts-ignore
|
|
17
|
-
const { apiDir, schemaDir, docsUrl, ui, outputFile, includeOpenApiRoutes } = this.
|
|
17
|
+
const { apiDir, schemaDir, docsUrl, ui, outputFile, includeOpenApiRoutes, schemaType = "typescript", } = this.config;
|
|
18
18
|
return {
|
|
19
19
|
apiDir,
|
|
20
20
|
schemaDir,
|
|
@@ -22,12 +22,51 @@ export class OpenApiGenerator {
|
|
|
22
22
|
ui,
|
|
23
23
|
outputFile,
|
|
24
24
|
includeOpenApiRoutes,
|
|
25
|
+
schemaType,
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
generate() {
|
|
28
29
|
const { apiDir } = this.config;
|
|
30
|
+
// Check if app router structure exists
|
|
31
|
+
let appRouterApiDir = "";
|
|
32
|
+
if (fs.existsSync(path.join(path.dirname(apiDir), "app", "api"))) {
|
|
33
|
+
appRouterApiDir = path.join(path.dirname(apiDir), "app", "api");
|
|
34
|
+
console.log(`Found app router API directory at ${appRouterApiDir}`);
|
|
35
|
+
}
|
|
36
|
+
// Scan pages router routes
|
|
29
37
|
this.routeProcessor.scanApiRoutes(apiDir);
|
|
38
|
+
// If app router directory exists, scan it as well
|
|
39
|
+
if (appRouterApiDir) {
|
|
40
|
+
this.routeProcessor.scanApiRoutes(appRouterApiDir);
|
|
41
|
+
}
|
|
30
42
|
this.template.paths = this.routeProcessor.getSwaggerPaths();
|
|
43
|
+
// Add server URL for examples if not already defined
|
|
44
|
+
if (!this.template.servers || this.template.servers.length === 0) {
|
|
45
|
+
this.template.servers = [
|
|
46
|
+
{
|
|
47
|
+
url: this.template.basePath || "",
|
|
48
|
+
description: "API server",
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
// Ensure there's a components section if not already defined
|
|
53
|
+
if (!this.template.components) {
|
|
54
|
+
this.template.components = {};
|
|
55
|
+
}
|
|
56
|
+
// Add schemas section if not already defined
|
|
57
|
+
if (!this.template.components.schemas) {
|
|
58
|
+
this.template.components.schemas = {};
|
|
59
|
+
}
|
|
60
|
+
// Get defined schemas from the processor
|
|
61
|
+
const definedSchemas = this.routeProcessor
|
|
62
|
+
.getSchemaProcessor()
|
|
63
|
+
.getDefinedSchemas();
|
|
64
|
+
if (definedSchemas && Object.keys(definedSchemas).length > 0) {
|
|
65
|
+
this.template.components.schemas = {
|
|
66
|
+
...this.template.components.schemas,
|
|
67
|
+
...definedSchemas,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
31
70
|
const openapiSpec = cleanSpec(this.template);
|
|
32
71
|
return openapiSpec;
|
|
33
72
|
}
|
|
@@ -18,6 +18,12 @@ export class RouteProcessor {
|
|
|
18
18
|
this.config = config;
|
|
19
19
|
this.schemaProcessor = new SchemaProcessor(config.schemaDir);
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Get the SchemaProcessor instance
|
|
23
|
+
*/
|
|
24
|
+
getSchemaProcessor() {
|
|
25
|
+
return this.schemaProcessor;
|
|
26
|
+
}
|
|
21
27
|
isRoute(varName) {
|
|
22
28
|
return HTTP_METHODS.includes(varName);
|
|
23
29
|
}
|
|
@@ -39,8 +45,16 @@ export class RouteProcessor {
|
|
|
39
45
|
if (this.isRoute(declaration.id.name)) {
|
|
40
46
|
// Don't bother adding routes for processing if only including OpenAPI routes and the route is not OpenAPI
|
|
41
47
|
if (!this.config.includeOpenApiRoutes ||
|
|
42
|
-
(this.config.includeOpenApiRoutes && dataTypes.isOpenApi))
|
|
48
|
+
(this.config.includeOpenApiRoutes && dataTypes.isOpenApi)) {
|
|
49
|
+
// Check for URL parameters in the route path
|
|
50
|
+
const routePath = this.getRoutePath(filePath);
|
|
51
|
+
const pathParams = extractPathParameters(routePath);
|
|
52
|
+
// If we have path parameters but no pathParamsType defined, we should log a warning
|
|
53
|
+
if (pathParams.length > 0 && !dataTypes.pathParams) {
|
|
54
|
+
console.warn(`Route ${routePath} contains path parameters ${pathParams.join(", ")} but no @pathParams type is defined.`);
|
|
55
|
+
}
|
|
43
56
|
this.addRouteToPaths(declaration.id.name, filePath, dataTypes);
|
|
57
|
+
}
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
60
|
if (t.isVariableDeclaration(declaration)) {
|
|
@@ -50,8 +64,14 @@ export class RouteProcessor {
|
|
|
50
64
|
const dataTypes = extractJSDocComments(path);
|
|
51
65
|
// Don't bother adding routes for processing if only including OpenAPI routes and the route is not OpenAPI
|
|
52
66
|
if (!this.config.includeOpenApiRoutes ||
|
|
53
|
-
(this.config.includeOpenApiRoutes && dataTypes.isOpenApi))
|
|
67
|
+
(this.config.includeOpenApiRoutes && dataTypes.isOpenApi)) {
|
|
68
|
+
const routePath = this.getRoutePath(filePath);
|
|
69
|
+
const pathParams = extractPathParameters(routePath);
|
|
70
|
+
if (pathParams.length > 0 && !dataTypes.pathParams) {
|
|
71
|
+
console.warn(`Route ${routePath} contains path parameters ${pathParams.join(", ")} but no @pathParams type is defined.`);
|
|
72
|
+
}
|
|
54
73
|
this.addRouteToPaths(decl.id.name, filePath, dataTypes);
|
|
74
|
+
}
|
|
55
75
|
}
|
|
56
76
|
}
|
|
57
77
|
});
|
|
@@ -103,6 +123,7 @@ export class RouteProcessor {
|
|
|
103
123
|
summary: summary,
|
|
104
124
|
description: description,
|
|
105
125
|
tags: [rootPath],
|
|
126
|
+
parameters: [],
|
|
106
127
|
};
|
|
107
128
|
// Add auth
|
|
108
129
|
if (auth) {
|
|
@@ -112,7 +133,6 @@ export class RouteProcessor {
|
|
|
112
133
|
},
|
|
113
134
|
];
|
|
114
135
|
}
|
|
115
|
-
definition.parameters = [];
|
|
116
136
|
if (params) {
|
|
117
137
|
definition.parameters =
|
|
118
138
|
this.schemaProcessor.createRequestParamsSchema(params);
|