exisjs 0.7.7 → 0.7.8

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.
@@ -138,7 +138,7 @@ export const UserParamsSchema = tex.object({ id: tex.string() });
138
138
  ```
139
139
 
140
140
  `tex` is the only validation engine. Schemas here are also the source for
141
- OpenAPI generation once `@exisjs/swagger` is installed.
141
+ OpenAPI generation via `exisjs/swagger`.
142
142
 
143
143
  ---
144
144
 
@@ -5,7 +5,7 @@ nextTitle: "Swagger API Reference"
5
5
 
6
6
  # Swagger & OpenAPI 3.1 Documentation
7
7
 
8
- ExisJS comes with a native, zero-bloat **Swagger & OpenAPI 3.1** documentation engine. It automatically discovers all registered routes, introspects **Tex** and **Zod** validation schemas, and serves interactive API documentation at `/docs`.
8
+ ExisJS comes with a native, built-in **Swagger & OpenAPI 3.1** documentation engine. It automatically discovers all registered routes, introspects **Tex** and **Zod** validation schemas, and serves interactive API documentation at `/docs`.
9
9
 
10
10
  ---
11
11
 
@@ -44,6 +44,8 @@ The industry-standard interactive Swagger explorer featuring **Try it out**, liv
44
44
  A modern, high-performance, dark-mode-first API reference documentation viewer with integrated code snippets and fast search.
45
45
 
46
46
  ```typescript filename="exis.config.ts"
47
+ import { defineConfig } from 'exisjs/config'
48
+
47
49
  export default defineConfig({
48
50
  docs: {
49
51
  enabled: true,
@@ -56,14 +58,14 @@ export default defineConfig({
56
58
 
57
59
  ## Documenting Functional Routes
58
60
 
59
- When writing functional route controllers (`export default controller({ ... })`), add metadata directly to the route definition:
61
+ In ExisJS folder-based routing, each endpoint folder contains a `route.ts` file that exports a default `controller({ ... })`. You can add OpenAPI metadata directly to each route definition:
60
62
 
61
63
  ```typescript filename="src/http/users/route.ts"
62
64
  import { controller, route } from 'exisjs/router'
63
65
  import { tex, getPaginationSkip } from 'exisjs/validator'
64
66
 
65
67
  export default controller({
66
- listUsers: route.get('/', {
68
+ list: route.get('/', {
67
69
  summary: 'List active users',
68
70
  description: 'Returns a paginated list of all active users in the system.',
69
71
  tags: ['Users'],
@@ -80,11 +82,19 @@ export default controller({
80
82
  },
81
83
  async handle({ query }) {
82
84
  const { skip, limit } = getPaginationSkip(query)
83
- return UserService.find(skip, limit)
85
+ return {
86
+ data: [
87
+ { id: '550e8400-e29b-41d4-a716-446655440000', name: 'Alice' },
88
+ { id: '550e8400-e29b-41d4-a716-446655440001', name: 'Bob' },
89
+ ],
90
+ total: 2,
91
+ skip,
92
+ limit,
93
+ }
84
94
  },
85
95
  }),
86
96
 
87
- createUser: route.post('/', {
97
+ create: route.post('/', {
88
98
  summary: 'Create a new user',
89
99
  tags: ['Users'],
90
100
  body: tex.object({
@@ -96,11 +106,11 @@ export default controller({
96
106
  400: { description: 'Validation error' },
97
107
  },
98
108
  async handle({ body }) {
99
- return UserService.create(body)
109
+ return { id: '550e8400-e29b-41d4-a716-446655440002', ...body }
100
110
  },
101
111
  }),
102
112
 
103
- getUser: route.get('/:id', {
113
+ getById: route.get('/:id', {
104
114
  summary: 'Get user by UUID',
105
115
  tags: ['Users'],
106
116
  params: tex.object({ id: tex.uuid() }),
@@ -109,7 +119,7 @@ export default controller({
109
119
  404: { description: 'User not found' },
110
120
  },
111
121
  async handle({ params }) {
112
- return UserService.findById(params.id)
122
+ return { id: params.id, name: 'Alice' }
113
123
  },
114
124
  }),
115
125
  })
@@ -119,9 +129,9 @@ export default controller({
119
129
 
120
130
  ## Documenting Class-Based Controllers (OOP)
121
131
 
122
- For class-based controllers, use decorators from `exisjs/swagger` or `exisjs/decorators`:
132
+ For class-based controllers in `route.ts`, use OpenAPI decorators from `exisjs/swagger` alongside standard routing decorators:
123
133
 
124
- ```typescript filename="src/http/products/controller.ts"
134
+ ```typescript filename="src/http/products/route.ts"
125
135
  import { Controller, Get, Post, Body, Param, Query } from 'exisjs/decorators'
126
136
  import {
127
137
  ApiTags,
@@ -130,32 +140,42 @@ import {
130
140
  ApiBearerAuth,
131
141
  ApiExclude,
132
142
  } from 'exisjs/swagger'
133
- import { tex } from 'exisjs/validator'
134
143
 
135
144
  @ApiTags('Products')
136
145
  @ApiBearerAuth()
137
- @Controller('/products')
138
- export class ProductController {
146
+ @Controller()
147
+ export default class ProductsController {
139
148
  @Get('/')
140
149
  @ApiOperation({
141
150
  summary: 'List products',
142
151
  description: 'Fetches paginated list of catalog products',
143
152
  })
144
153
  @ApiResponse({ status: 200, description: 'List of products' })
145
- async list(@Query('page') page: number) {
146
- return ProductService.findAll(page)
154
+ async list(@Query('page') page: number = 1) {
155
+ return [
156
+ { id: 1, name: 'Mechanical Keyboard', price: 99 },
157
+ { id: 2, name: 'Ergonomic Mouse', price: 49 },
158
+ ]
147
159
  }
148
160
 
149
161
  @Post('/')
150
162
  @ApiOperation({ summary: 'Create new product' })
151
163
  @ApiResponse({ status: 201, description: 'Product created' })
152
- async create(@Body() dto: any) {
153
- return ProductService.create(dto)
164
+ async create(@Body() dto: { name: string; price: number }) {
165
+ return { id: 3, ...dto }
166
+ }
167
+
168
+ @Get('/:id')
169
+ @ApiOperation({ summary: 'Get product by ID' })
170
+ @ApiResponse({ status: 200, description: 'Product found' })
171
+ @ApiResponse({ status: 404, description: 'Product not found' })
172
+ async getById(@Param('id') id: string) {
173
+ return { id, name: 'Mechanical Keyboard', price: 99 }
154
174
  }
155
175
 
156
- @Get('/internal-stats')
176
+ @Get('/internal/metrics')
157
177
  @ApiExclude() // Excluded from Swagger documentation
158
- async stats() {
178
+ async metrics() {
159
179
  return { uptime: process.uptime() }
160
180
  }
161
181
  }
@@ -165,7 +185,7 @@ export class ProductController {
165
185
 
166
186
  ## Cascading Boundary Metadata
167
187
 
168
- Cascading boundaries (`boundary.ts`) can assign shared tags, descriptions, or security requirements to all nested routes:
188
+ Cascading boundaries (`boundary.ts`) can assign shared tags, descriptions, or security requirements to all nested routes in the directory:
169
189
 
170
190
  ```typescript filename="src/http/admin/boundary.ts"
171
191
  import { defineBoundary } from 'exisjs/router'
@@ -183,6 +203,8 @@ export const config = defineBoundary({
183
203
  Define reusable security schemes (such as Bearer JWT, API Keys, or OAuth2) in `exis.config.ts`:
184
204
 
185
205
  ```typescript filename="exis.config.ts"
206
+ import { defineConfig } from 'exisjs/config'
207
+
186
208
  export default defineConfig({
187
209
  docs: {
188
210
  enabled: true,
@@ -208,21 +230,60 @@ export default defineConfig({
208
230
 
209
231
  ---
210
232
 
211
- ## Programmatic / Custom Setup
233
+ ## Advanced Configuration (`exis.config.ts`)
212
234
 
213
- You can also mount Swagger programmatically on any `App` instance:
235
+ For advanced customization, such as modifying the generated OpenAPI spec before serving or injecting custom styling:
214
236
 
215
- ```typescript filename="src/index.ts"
216
- import { App } from 'exisjs'
217
- import { swagger } from 'exisjs/swagger'
237
+ ```typescript filename="exis.config.ts"
238
+ import { defineConfig } from 'exisjs/config'
218
239
 
219
- const app = new App()
240
+ export default defineConfig({
241
+ docs: {
242
+ enabled: true,
243
+ title: 'Enterprise API Gateway',
244
+ version: '2.0.0',
245
+ description: 'Internal and external microservices API reference',
246
+ ui: 'scalar', // 'swagger-ui' | 'scalar'
247
+ servers: [
248
+ { url: 'https://api.example.com', description: 'Production Server' },
249
+ { url: 'http://localhost:3000', description: 'Local Development' },
250
+ ],
251
+ transformSpec(spec) {
252
+ // Programmatically mutate or enrich OpenAPI specification
253
+ spec.info.contact = { name: 'API Support', email: 'support@example.com' }
254
+ return spec
255
+ },
256
+ },
257
+ })
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Server Lifecycle Hook (`server.ts`)
263
+
264
+ You can also interact with the Swagger subsystem or application instance during server boot in `src/http/server.ts`:
265
+
266
+ ### Functional Paradigm
267
+ ```typescript filename="src/http/server.ts"
268
+ import { exis } from 'exisjs'
269
+
270
+ export default exis({
271
+ onStart(app) {
272
+ console.log('[API] Swagger documentation ready at /docs')
273
+ },
274
+ })
275
+ ```
276
+
277
+ ### Class-Based (OOP) Paradigm
278
+ ```typescript filename="src/http/server.ts"
279
+ import { Server } from 'exisjs/decorators'
280
+ import type { App } from 'exisjs'
220
281
 
221
- // Mount custom swagger documentation
222
- swagger({
223
- title: 'Custom API',
224
- version: '2.0.0',
225
- path: '/api/docs',
226
- specPath: '/api/docs/spec.json',
227
- })(app)
282
+ @Server()
283
+ export default class RootServer {
284
+ async onStart(app: App) {
285
+ console.log('[API] Swagger documentation ready at /docs')
286
+ }
287
+ }
228
288
  ```
289
+
@@ -53,91 +53,174 @@ import {
53
53
  Assigns one or more tags to a controller class or method handler.
54
54
  *(Alias: `@Tags()`)*
55
55
 
56
- ```typescript
56
+ ```typescript filename="src/http/users/route.ts"
57
+ import { Controller, Get } from 'exisjs/decorators'
58
+ import { ApiTags } from 'exisjs/swagger'
59
+
57
60
  @ApiTags('Users', 'Authentication')
58
- @Controller('/users')
59
- export class UserController {}
61
+ @Controller()
62
+ export default class UserController {
63
+ @Get('/')
64
+ findAll() {
65
+ return []
66
+ }
67
+ }
60
68
  ```
61
69
 
62
70
  ### `@ApiOperation(options)`
63
71
  Attaches summary, description, operationId, and deprecated status to a route method.
64
72
  *(Alias: `@Operation()`)*
65
73
 
66
- ```typescript
67
- @Get('/:id')
68
- @ApiOperation({
69
- summary: 'Get user by UUID',
70
- description: 'Fetches user details from database',
71
- deprecated: false,
72
- })
73
- getUser() {}
74
+ ```typescript filename="src/http/users/route.ts"
75
+ import { Controller, Get, Param } from 'exisjs/decorators'
76
+ import { ApiOperation } from 'exisjs/swagger'
77
+
78
+ @Controller()
79
+ export default class UserController {
80
+ @Get('/:id')
81
+ @ApiOperation({
82
+ summary: 'Get user by UUID',
83
+ description: 'Fetches user details from database',
84
+ deprecated: false,
85
+ })
86
+ getUser(@Param('id') id: string) {
87
+ return { id, name: 'Alice' }
88
+ }
89
+ }
74
90
  ```
75
91
 
76
92
  ### `@ApiResponse(options)`
77
93
  Defines an expected HTTP response status code and optional schema.
78
94
 
79
- ```typescript
80
- @ApiResponse({
81
- status: 200,
82
- description: 'User details found',
83
- schema: UserResponseSchema,
95
+ ```typescript filename="src/http/users/route.ts"
96
+ import { Controller, Get, Param } from 'exisjs/decorators'
97
+ import { ApiOperation, ApiResponse } from 'exisjs/swagger'
98
+ import { tex } from 'exisjs/validator'
99
+
100
+ const UserSchema = tex.object({
101
+ id: tex.uuid(),
102
+ name: tex.string(),
84
103
  })
85
- @ApiResponse({ status: 404, description: 'User not found' })
86
- getUser() {}
104
+
105
+ @Controller()
106
+ export default class UserController {
107
+ @Get('/:id')
108
+ @ApiResponse({
109
+ status: 200,
110
+ description: 'User details found',
111
+ schema: UserSchema,
112
+ })
113
+ @ApiResponse({ status: 404, description: 'User not found' })
114
+ getUser(@Param('id') id: string) {
115
+ return { id, name: 'Alice' }
116
+ }
117
+ }
87
118
  ```
88
119
 
89
120
  ### `@ApiBearerAuth(name?: string)`
90
121
  Requires Bearer JWT authentication for this endpoint or controller.
91
122
  *(Alias: `@BearerAuth()`)*
92
123
 
93
- ```typescript
124
+ ```typescript filename="src/http/users/route.ts"
125
+ import { Controller, Get } from 'exisjs/decorators'
126
+ import { ApiBearerAuth } from 'exisjs/swagger'
127
+
94
128
  @ApiBearerAuth()
95
- @Get('/me')
96
- getMe() {}
129
+ @Controller()
130
+ export default class UserController {
131
+ @Get('/me')
132
+ getMe() {
133
+ return { id: 'user-123', role: 'admin' }
134
+ }
135
+ }
97
136
  ```
98
137
 
99
138
  ### `@ApiSecurity(name: string, scopes?: string[])`
100
139
  Requires custom security scheme authentication for this endpoint or controller.
101
140
 
102
- ```typescript
103
- @ApiSecurity('apiKeyAuth')
104
- @Get('/metrics')
105
- getMetrics() {}
141
+ ```typescript filename="src/http/metrics/route.ts"
142
+ import { Controller, Get } from 'exisjs/decorators'
143
+ import { ApiSecurity } from 'exisjs/swagger'
144
+
145
+ @Controller()
146
+ export default class MetricsController {
147
+ @Get('/')
148
+ @ApiSecurity('apiKeyAuth')
149
+ getMetrics() {
150
+ return { uptime: process.uptime() }
151
+ }
152
+ }
106
153
  ```
107
154
 
108
155
  ### `@ApiExclude()`
109
156
  Excludes an endpoint or controller class from OpenAPI and Swagger generation.
110
157
 
111
- ```typescript
112
- @Get('/internal')
113
- @ApiExclude()
114
- getInternal() {}
158
+ ```typescript filename="src/http/internal/route.ts"
159
+ import { Controller, Get } from 'exisjs/decorators'
160
+ import { ApiExclude } from 'exisjs/swagger'
161
+
162
+ @Controller()
163
+ export default class InternalController {
164
+ @Get('/health')
165
+ @ApiExclude()
166
+ getHealth() {
167
+ return { status: 'healthy' }
168
+ }
169
+ }
115
170
  ```
116
171
 
117
172
  ### `@ApiProperty(options?: { description?: string; example?: any; required?: boolean })`
118
173
  Annotates a class property with metadata for OpenAPI schema models.
119
174
 
175
+ ```typescript filename="src/http/users/dto.ts"
176
+ import { ApiProperty } from 'exisjs/swagger'
177
+
178
+ export class CreateUserDto {
179
+ @ApiProperty({ description: 'Full name of the user', example: 'Alice Johnson', required: true })
180
+ name!: string
181
+
182
+ @ApiProperty({ description: 'Work email address', example: 'alice@example.com', required: true })
183
+ email!: string
184
+ }
185
+ ```
186
+
120
187
  ---
121
188
 
122
189
  ## Functions
123
190
 
124
191
  ### `swagger(config?: SwaggerConfig)`
125
- Convenience helper to mount Swagger/OpenAPI documentation on an ExisJS App instance.
192
+ Convenience helper to mount Swagger/OpenAPI documentation on an ExisJS App instance (typically inside `onStart` or custom server setups).
126
193
 
127
- ```typescript
128
- import { App } from 'exisjs'
194
+ ```typescript filename="src/http/server.ts"
195
+ import { exis } from 'exisjs'
129
196
  import { swagger } from 'exisjs/swagger'
130
197
 
131
- const app = new App()
132
- swagger({ title: 'My API', path: '/docs' })(app)
198
+ export default exis({
199
+ onStart(app) {
200
+ swagger({ title: 'My API', path: '/docs' })(app)
201
+ },
202
+ })
133
203
  ```
134
204
 
135
205
  ### `generateOpenApiSpec(routesOrApp, config)`
136
- Pure generator function that introspects routes, parameters, Tex/Zod schemas, and returns an `OpenApiSpec` object.
206
+ Pure generator function that introspects routes, parameters, and `TexEngine`/Zod schemas and returns a complete `OpenApiSpec` object.
137
207
 
138
208
  ```typescript
209
+ import { generateOpenApiSpec } from 'exisjs/swagger'
210
+
139
211
  const spec = generateOpenApiSpec(app, { title: 'My API', version: '1.0.0' })
140
212
  ```
141
213
 
142
214
  ### `renderDocumentationHtml(config)`
143
215
  Renders standalone HTML markup for Swagger UI or Scalar API reference.
216
+
217
+ ```typescript
218
+ import { renderDocumentationHtml } from 'exisjs/swagger'
219
+
220
+ const html = renderDocumentationHtml({
221
+ title: 'My API',
222
+ specPath: '/docs/json',
223
+ ui: 'scalar',
224
+ })
225
+ ```
226
+
@@ -234,7 +234,7 @@ type Profile = ResolveSchema<typeof ProfileSchema>
234
234
 
235
235
  ## OpenAPI Generation
236
236
 
237
- The `TexEngine` automatically converts validation schemas into strictly compliant OpenAPI/Swagger JSON schemas when attached to routes. This powers zero-config Swagger documentation once `@exisjs/swagger` is added.
237
+ The `TexEngine` automatically converts validation schemas into strictly compliant OpenAPI/Swagger JSON schemas when attached to routes. This powers zero-config Swagger documentation in `exisjs/swagger`.
238
238
 
239
239
  ---
240
240
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exisjs",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "description": "efficient full-stack web framework for Node.js",
5
5
  "author": "V25 Group <support@v25group.com>",
6
6
  "keywords": [
@@ -84,6 +84,10 @@
84
84
  "./logger": {
85
85
  "types": "./dist/logger/index.d.ts",
86
86
  "default": "./dist/logger/index.js"
87
+ },
88
+ "./swagger": {
89
+ "types": "./dist/swagger/index.d.ts",
90
+ "default": "./dist/swagger/index.js"
87
91
  }
88
92
  },
89
93
  "files": [