exisjs 0.7.12 → 0.7.13
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 +195 -39
- package/dist/config/types.d.ts +2 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/decorators/params.d.ts +21 -0
- package/dist/decorators/params.d.ts.map +1 -1
- package/dist/decorators/params.js +21 -1
- package/dist/decorators/params.js.map +1 -1
- package/dist/di/container.d.ts +15 -6
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +26 -3
- package/dist/di/container.js.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/index.js.map +1 -1
- package/dist/di/inject.d.ts +2 -1
- package/dist/di/inject.d.ts.map +1 -1
- package/dist/di/inject.js +2 -1
- package/dist/di/inject.js.map +1 -1
- package/dist/docs/BENCHMARKS.md +4 -4
- package/dist/docs/content/README.md +43 -44
- package/dist/docs/content/boundaries.mdx +138 -61
- package/dist/docs/content/cli/commands.mdx +151 -128
- package/dist/docs/content/cli/overview.mdx +61 -47
- package/dist/docs/content/cli/usage.mdx +107 -30
- package/dist/docs/content/config.mdx +145 -158
- package/dist/docs/content/controllers.mdx +193 -464
- package/dist/docs/content/cron.mdx +183 -69
- package/dist/docs/content/decorators.mdx +449 -154
- package/dist/docs/content/errors.mdx +202 -305
- package/dist/docs/content/exisjs-mcp.mdx +59 -49
- package/dist/docs/content/installation.mdx +243 -157
- package/dist/docs/content/introduction.mdx +220 -48
- package/dist/docs/content/middleware.mdx +171 -497
- package/dist/docs/content/modules.mdx +141 -83
- package/dist/docs/content/providers.mdx +216 -180
- package/dist/docs/content/requests.mdx +168 -488
- package/dist/docs/content/responses.mdx +200 -533
- package/dist/docs/content/routing.mdx +235 -598
- package/dist/docs/content/security.mdx +233 -57
- package/dist/docs/content/structure.mdx +151 -240
- package/dist/docs/content/swagger.mdx +192 -91
- package/dist/docs/content/testing.mdx +205 -74
- package/dist/docs/content/validation.mdx +479 -341
- package/dist/docs/reference/README.md +34 -2
- package/dist/docs/reference/config.mdx +277 -75
- package/dist/docs/reference/context.mdx +197 -106
- package/dist/docs/reference/cron.mdx +266 -152
- package/dist/docs/reference/database.mdx +135 -53
- package/dist/docs/reference/decorators.mdx +234 -116
- package/dist/docs/reference/di.mdx +146 -112
- package/dist/docs/reference/error.mdx +155 -178
- package/dist/docs/reference/exis.mdx +141 -70
- package/dist/docs/reference/logger.mdx +110 -126
- package/dist/docs/reference/middleware.mdx +235 -158
- package/dist/docs/reference/plugin.mdx +136 -64
- package/dist/docs/reference/router.mdx +275 -293
- package/dist/docs/reference/sanitize.mdx +91 -87
- package/dist/docs/reference/swagger.mdx +280 -100
- package/dist/docs/reference/testing.mdx +223 -89
- package/dist/docs/reference/validator.mdx +194 -206
- package/dist/lib/start-server.js +3 -8
- package/dist/lib/start-server.js.map +1 -1
- package/dist/module/module.d.ts +6 -3
- package/dist/module/module.d.ts.map +1 -1
- package/dist/module/module.js +17 -3
- package/dist/module/module.js.map +1 -1
- package/dist/router/controller-registrar.d.ts.map +1 -1
- package/dist/router/controller-registrar.js +26 -4
- package/dist/router/controller-registrar.js.map +1 -1
- package/dist/server/app.d.ts +3 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +24 -8
- package/dist/server/app.js.map +1 -1
- package/dist/server/request.d.ts +2 -0
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +11 -0
- package/dist/server/request.js.map +1 -1
- package/dist/swagger/generator.d.ts.map +1 -1
- package/dist/swagger/generator.js +130 -10
- package/dist/swagger/generator.js.map +1 -1
- package/dist/telemetry/index.d.ts +12 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +121 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/testing/client.d.ts +1 -0
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +4 -1
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +11 -4
- package/dist/testing/index.js.map +1 -1
- package/dist/validator/builder.d.ts +26 -1
- package/dist/validator/builder.d.ts.map +1 -1
- package/dist/validator/builder.js +31 -0
- package/dist/validator/builder.js.map +1 -1
- package/dist/validator/engine.d.ts +23 -0
- package/dist/validator/engine.d.ts.map +1 -1
- package/dist/validator/engine.js +228 -31
- package/dist/validator/engine.js.map +1 -1
- package/dist/validator/index.d.ts +8 -4
- package/dist/validator/index.d.ts.map +1 -1
- package/dist/validator/index.js +4 -1
- package/dist/validator/index.js.map +1 -1
- package/dist/validator/tex-types.d.ts +15 -0
- package/dist/validator/tex-types.d.ts.map +1 -1
- package/dist/validator/tex-types.js +102 -0
- package/dist/validator/tex-types.js.map +1 -1
- package/dist/validator/tex.d.ts +1 -1
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js +3 -1
- package/dist/validator/tex.js.map +1 -1
- package/dist/validator/types.d.ts +1 -0
- package/dist/validator/types.d.ts.map +1 -1
- package/package.json +46 -14
- package/dist/docs/content/dataloader.mdx +0 -170
- package/dist/docs/reference/dataloader.mdx +0 -91
|
@@ -1,259 +1,278 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
3
|
-
description: "API Reference for
|
|
2
|
+
title: "exisjs/router"
|
|
3
|
+
description: "API Reference for ExisJS routing engines: Functional routing (controller, route.*, defineBoundary) and Class-Based OOP routing (decorators, parameter injectors)."
|
|
4
|
+
nextUrl: "/docs/reference/validator"
|
|
5
|
+
nextTitle: "Validator Reference"
|
|
4
6
|
---
|
|
5
7
|
|
|
6
|
-
#
|
|
8
|
+
# `exisjs/router`
|
|
7
9
|
|
|
8
|
-
ExisJS provides two
|
|
10
|
+
ExisJS provides two routing paradigms with full feature parity: **Functional Routing** (`exisjs/router`) and **Class-Based OOP Routing** (`exisjs/decorators`).
|
|
9
11
|
|
|
10
12
|
---
|
|
11
13
|
|
|
12
|
-
## 1. Functional Routing
|
|
14
|
+
## 1. Functional Routing API
|
|
13
15
|
|
|
14
|
-
The functional router
|
|
15
|
-
|
|
16
|
-
These utilities allow you to define HTTP endpoints with built-in validation schemas, automatically typed execution contexts, and file-level configuration.
|
|
17
|
-
|
|
18
|
-
### `controller()`
|
|
19
|
-
|
|
20
|
-
The `controller` function wraps a collection of routes and configuration options (like middleware or CORS) inside a single file.
|
|
21
|
-
|
|
22
|
-
### Usage
|
|
16
|
+
The functional router allows defining endpoints and boundaries using composable objects and functions.
|
|
23
17
|
|
|
24
18
|
```typescript
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
19
|
+
import {
|
|
20
|
+
route,
|
|
21
|
+
controller,
|
|
22
|
+
createRouter,
|
|
23
|
+
defineMiddleware,
|
|
24
|
+
defineBoundary,
|
|
25
|
+
validate,
|
|
26
|
+
getContext,
|
|
27
|
+
setContext,
|
|
28
|
+
getRequest,
|
|
29
|
+
getResponse,
|
|
30
|
+
after,
|
|
31
|
+
fileUpload,
|
|
32
|
+
SSEStream,
|
|
33
|
+
} from 'exisjs/router'
|
|
34
|
+
|
|
35
|
+
import type {
|
|
36
|
+
RouteConfig,
|
|
37
|
+
RouteDefinition,
|
|
38
|
+
ControllerConfig,
|
|
39
|
+
SuperContext,
|
|
40
|
+
BoundaryConfig,
|
|
41
|
+
BoundaryContext,
|
|
42
|
+
BoundaryExcludeRule,
|
|
43
|
+
Request,
|
|
44
|
+
Response,
|
|
45
|
+
NextFunction,
|
|
46
|
+
} from 'exisjs/router'
|
|
40
47
|
```
|
|
41
48
|
|
|
42
|
-
###
|
|
43
|
-
- `cors` (`boolean | CorsConfig`): Enables or configures CORS for all routes in this file.
|
|
44
|
-
- `middleware` (`Handler[]`): Array of middleware functions applied to all routes in this file.
|
|
45
|
-
- `filters` (`any | any[]`): Route filters applied to all routes.
|
|
46
|
-
- `onError` (`HookError`): A file-level error handler.
|
|
47
|
-
- `onResponse` (`HookResponse`): A file-level response hook.
|
|
48
|
-
- `[key: string]` (`RouteDefinition`): **Any other key** is treated as a route definition (e.g. `getUser: route.get(...)`). This is how you bind routes to the controller!
|
|
49
|
-
|
|
50
|
-
### `controller.merge(...controllers)`
|
|
49
|
+
### `controller(config)`
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
Wraps route definitions, file-scoped middlewares, guards, and CORS policies into an exportable controller slice.
|
|
53
52
|
|
|
54
|
-
```typescript
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
login: route.post('/login', {
|
|
60
|
-
async handle({ body }) {
|
|
61
|
-
return { token: 'jwt-token' }
|
|
62
|
-
}
|
|
63
|
-
}),
|
|
64
|
-
logout: route.post('/logout', {
|
|
65
|
-
async handle() {
|
|
66
|
-
return { success: true }
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
})
|
|
53
|
+
```typescript
|
|
54
|
+
function controller<T extends Record<string, any>>(
|
|
55
|
+
config: ControllerConfig<T>
|
|
56
|
+
): ControllerDefinition<T>
|
|
57
|
+
```
|
|
70
58
|
|
|
71
|
-
|
|
72
|
-
const oauthRoutes = controller({
|
|
73
|
-
google: route.get('/google', {
|
|
74
|
-
async handle() {
|
|
75
|
-
return { redirectUrl: 'https://accounts.google.com/o/oauth2/auth' }
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
})
|
|
59
|
+
#### `ControllerConfig` Options
|
|
79
60
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
61
|
+
| Property | Type | Description |
|
|
62
|
+
| :--- | :--- | :--- |
|
|
63
|
+
| `cors` | `boolean \| CorsConfig` | CORS configuration applied to all routes in this controller file. |
|
|
64
|
+
| `middleware` / `middlewares` | `Handler \| Handler[]` | Middleware functions executed prior to controller route handlers. |
|
|
65
|
+
| `guards` | `Guard[]` | Authorization guards executed before controller endpoints. |
|
|
66
|
+
| `interceptors` | `Interceptor[]` | Response interceptors applied to this controller. |
|
|
67
|
+
| `filters` | `Filter[]` | Exception filters applied across this controller. |
|
|
68
|
+
| `onError` | `(err: Error, req: Request, res: Response) => any` | Controller-scoped error hook. |
|
|
69
|
+
| `onResponse` | `(data: any, req: Request, res: Response) => any` | Controller-scoped response hook. |
|
|
70
|
+
| `[key: string]` | `RouteDefinition` | Route builder definitions (e.g. `list: route.get(...)`). |
|
|
83
71
|
|
|
84
|
-
|
|
72
|
+
#### Composition Methods
|
|
85
73
|
|
|
86
|
-
|
|
74
|
+
- **`controller.merge(...controllers)`**: Combines multiple functional controller slices into a single unified controller.
|
|
75
|
+
- **`controller.extend(base, overrides)`**: Inherits routes and configuration from a base controller, allowing overrides and additions.
|
|
87
76
|
|
|
88
|
-
```typescript
|
|
77
|
+
```typescript
|
|
89
78
|
import { controller, route } from 'exisjs/router'
|
|
90
79
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return controller({
|
|
94
|
-
list: route.get('/', {
|
|
95
|
-
async handle() {
|
|
96
|
-
return { resource, items: [] }
|
|
97
|
-
}
|
|
98
|
-
}),
|
|
99
|
-
getById: route.get('/:id', {
|
|
100
|
-
async handle({ params }) {
|
|
101
|
-
return { resource, id: params.id }
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Inherit base endpoints and add specialized or overridden routes
|
|
108
|
-
export default controller.extend(createBaseCrud('products'), {
|
|
109
|
-
// Additional specialized route
|
|
110
|
-
featured: route.get('/featured', {
|
|
80
|
+
const base = controller({
|
|
81
|
+
list: route.get('/', {
|
|
111
82
|
async handle() {
|
|
112
|
-
return {
|
|
113
|
-
}
|
|
83
|
+
return { items: [] }
|
|
84
|
+
},
|
|
114
85
|
}),
|
|
86
|
+
})
|
|
115
87
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
async handle({
|
|
119
|
-
return {
|
|
120
|
-
}
|
|
121
|
-
})
|
|
88
|
+
export default controller.extend(base, {
|
|
89
|
+
create: route.post('/', {
|
|
90
|
+
async handle({ body }) {
|
|
91
|
+
return { created: true }
|
|
92
|
+
},
|
|
93
|
+
}),
|
|
122
94
|
})
|
|
123
95
|
```
|
|
124
96
|
|
|
125
97
|
---
|
|
126
98
|
|
|
127
|
-
|
|
99
|
+
### `route` Builder
|
|
128
100
|
|
|
129
|
-
|
|
101
|
+
Constructs route definitions for standard HTTP methods and streaming protocols:
|
|
130
102
|
|
|
131
|
-
|
|
103
|
+
- `route.get(path, config)`
|
|
104
|
+
- `route.post(path, config)`
|
|
105
|
+
- `route.put(path, config)`
|
|
106
|
+
- `route.patch(path, config)`
|
|
107
|
+
- `route.delete(path, config)`
|
|
108
|
+
- `route.options(path, config)`
|
|
109
|
+
- `route.head(path, config)`
|
|
110
|
+
- `route.all(path, config)`
|
|
111
|
+
- `route.sse(path, config)`
|
|
112
|
+
- `route.ws(path, config)`
|
|
132
113
|
|
|
133
|
-
|
|
114
|
+
#### `RouteConfig` Specification
|
|
134
115
|
|
|
135
116
|
```typescript
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
117
|
+
export interface RouteConfig<B = any, Q = any, P = any, TContext = Record<string, any>, TReturn = any> {
|
|
118
|
+
// Input Validation Schemas
|
|
119
|
+
body?: Schema<B>
|
|
120
|
+
query?: Schema<Q>
|
|
121
|
+
params?: Schema<P>
|
|
122
|
+
response?: Schema<TReturn>
|
|
123
|
+
returns?: Schema<TReturn>
|
|
124
|
+
|
|
125
|
+
// Execution Handler
|
|
126
|
+
handle: (ctx: SuperContext<B, Q, P, TContext>) => TReturn | Promise<TReturn>
|
|
127
|
+
|
|
128
|
+
// Pipeline & Security
|
|
129
|
+
middleware?: Handler<any, any, any, any, TContext> | Handler<any, any, any, any, TContext>[]
|
|
130
|
+
guards?: any | any[]
|
|
131
|
+
interceptors?: any | any[]
|
|
132
|
+
filters?: any | any[]
|
|
133
|
+
roles?: string | string[]
|
|
134
|
+
permissions?: string | string[]
|
|
135
|
+
cors?: boolean | CorsConfig
|
|
136
|
+
host?: string | string[]
|
|
137
|
+
public?: boolean
|
|
138
|
+
|
|
139
|
+
// File Uploads & Tuning
|
|
140
|
+
upload?: string | RouteUploadOptions
|
|
141
|
+
timeout?: number | { ms: number; statusCode?: number; message?: string }
|
|
142
|
+
httpCode?: number
|
|
143
|
+
headers?: Record<string, string>
|
|
144
|
+
redirect?: string | { url: string; statusCode?: number }
|
|
145
|
+
|
|
146
|
+
// OpenAPI Metadata
|
|
147
|
+
summary?: string
|
|
148
|
+
description?: string
|
|
149
|
+
tags?: string[]
|
|
150
|
+
operationId?: string
|
|
151
|
+
deprecated?: boolean
|
|
152
|
+
excludeFromDocs?: boolean
|
|
153
|
+
}
|
|
152
154
|
```
|
|
153
155
|
|
|
154
|
-
### Options (`RouteConfig`)
|
|
155
|
-
|
|
156
|
-
- `body`: A validation schema for the request body (e.g., using `tex` or Zod).
|
|
157
|
-
- `query`: A validation schema for the query string (e.g., `tex.pagination()`).
|
|
158
|
-
- `params`: A validation schema for the URL path parameters.
|
|
159
|
-
- `upload` (`string | RouteUploadOptions`): Declarative multipart upload configuration (e.g. `{ field: 'photo', maxBytes: 10 * 1024 * 1024, mimeTypes: [...] }`). Automatically mounts `fileUpload` middleware and populates `req.file` / `req.files`.
|
|
160
|
-
- `timeout` (`number`): Per-route timeout override in milliseconds (e.g. `120000`). Overrides global timeout for this endpoint.
|
|
161
|
-
- `response`: A validation schema to serialize/validate the outgoing response.
|
|
162
|
-
- `middleware` (`Handler[]`): An array of middleware specific to this route.
|
|
163
|
-
- `cors` (`boolean | CorsConfig`): Route-specific CORS overrides.
|
|
164
|
-
- `filters` (`any | any[]`): Route-specific filters (like rate limiters or authorization checks).
|
|
165
|
-
- `permissions` (`string[]`): An array of required permission flags to access this route.
|
|
166
|
-
- `metadata` (`Record<string, any>`): Arbitrary metadata attached to the route for custom plugins/middleware.
|
|
167
|
-
- `host` (`string | string[]`): Restrict this route to a specific subdomain/host (e.g. `api.example.com`).
|
|
168
|
-
- `handle`: The async function that executes your business logic. It receives a `SuperContext`.
|
|
169
|
-
|
|
170
156
|
---
|
|
171
157
|
|
|
172
|
-
|
|
158
|
+
### `SuperContext`
|
|
173
159
|
|
|
174
|
-
|
|
160
|
+
The strongly-typed execution context supplied to every functional `handle()` function:
|
|
175
161
|
|
|
176
162
|
```typescript
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
})
|
|
204
|
-
})
|
|
163
|
+
export interface SuperContext<B = any, Q = any, P = any, TContext = Record<string, any>> {
|
|
164
|
+
/** Pre-validated request body */
|
|
165
|
+
body: B
|
|
166
|
+
/** Pre-validated query string parameters */
|
|
167
|
+
query: Q
|
|
168
|
+
/** Pre-validated URL route parameters */
|
|
169
|
+
params: P
|
|
170
|
+
/** Request HTTP headers */
|
|
171
|
+
headers: Record<string, string | string[] | undefined>
|
|
172
|
+
/** ExisRequest instance */
|
|
173
|
+
req: Request<B, Q, P, TContext>
|
|
174
|
+
/** ExisResponse instance */
|
|
175
|
+
res: Response
|
|
176
|
+
/** Exis Application instance */
|
|
177
|
+
app: App
|
|
178
|
+
/** Mutable dictionary shared across route middlewares */
|
|
179
|
+
state: Record<string, any>
|
|
180
|
+
/** Request-scoped Dependency Injection resolver */
|
|
181
|
+
resolve: <T>(token: ProviderToken<T>) => T
|
|
182
|
+
/** Single uploaded file */
|
|
183
|
+
file?: ExisFile
|
|
184
|
+
/** Multiple uploaded files */
|
|
185
|
+
files?: ExisFile[] | Record<string, ExisFile[]>
|
|
186
|
+
/** Multipart text fields */
|
|
187
|
+
fields?: Record<string, any>
|
|
188
|
+
}
|
|
205
189
|
```
|
|
206
190
|
|
|
207
191
|
---
|
|
208
192
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
If your application relies heavily on custom context (e.g., passing a `user` or `workspace` object injected via middleware to every route), you can create a strongly-typed custom router using `createRouter<T>()`.
|
|
193
|
+
### `defineBoundary(config)`
|
|
212
194
|
|
|
213
|
-
|
|
195
|
+
Declares hierarchical folder-level middleware, security controls, and response defaults inside `boundary.ts` files.
|
|
214
196
|
|
|
215
|
-
|
|
197
|
+
```typescript
|
|
198
|
+
function defineBoundary<TContext = Record<string, any>>(
|
|
199
|
+
config: BoundaryConfig<TContext>
|
|
200
|
+
): BoundaryConfig<TContext>
|
|
201
|
+
```
|
|
216
202
|
|
|
217
|
-
|
|
218
|
-
|
|
203
|
+
#### `BoundaryConfig` Options
|
|
204
|
+
|
|
205
|
+
| Property | Type | Description |
|
|
206
|
+
| :--- | :--- | :--- |
|
|
207
|
+
| `middleware` / `middlewares` | `Handler \| Handler[]` | Middleware cascaded to all routes in this directory and subdirectories. |
|
|
208
|
+
| `cors` | `boolean \| CorsConfig` | Directory-scoped CORS policy. |
|
|
209
|
+
| `headers` | `Record<string, string>` | Default response headers applied to matching routes. |
|
|
210
|
+
| `guards` | `Guard[]` | Authorization guards applied to matching routes. |
|
|
211
|
+
| `interceptors` | `Interceptor[]` | Response interceptors applied to matching routes. |
|
|
212
|
+
| `filters` | `Filter[]` | Exception filters applied to matching routes. |
|
|
213
|
+
| `exclude` | `(string \| BoundaryExcludeRule)[]` | Route patterns or method rules excluded from this boundary. |
|
|
214
|
+
| `timeout` | `number \| ((req: Request) => number)` | Timeout threshold in milliseconds for routes in this directory. |
|
|
215
|
+
| `blockSuspiciousProbes` | `boolean \| BlockProbesOptions` | Suppresses and blackholes malicious scanner probes (`.env`, `wp-admin`, `.git`). |
|
|
216
|
+
| `metadata` | `Record<string, any>` | Metadata (e.g. OpenAPI tags) cascaded to all child routes. |
|
|
219
217
|
|
|
220
|
-
|
|
221
|
-
userId: string;
|
|
222
|
-
role: string;
|
|
223
|
-
email: string;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
interface AppContext {
|
|
227
|
-
user: User;
|
|
228
|
-
workspace: string; // Just an example to prove we can pass any property
|
|
229
|
-
}
|
|
218
|
+
---
|
|
230
219
|
|
|
231
|
-
|
|
232
|
-
export const { route, controller } = createRouter<AppContext>();
|
|
233
|
-
```
|
|
220
|
+
## 2. Class-Based OOP Routing API (`exisjs/decorators`)
|
|
234
221
|
|
|
235
|
-
|
|
222
|
+
The Class-Based paradigm uses standard TypeScript decorators for controller definitions, route methods, parameter injection, and pipeline lifecycle hooks.
|
|
236
223
|
|
|
237
|
-
```typescript
|
|
238
|
-
import {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
224
|
+
```typescript
|
|
225
|
+
import {
|
|
226
|
+
Controller,
|
|
227
|
+
Get,
|
|
228
|
+
Post,
|
|
229
|
+
Put,
|
|
230
|
+
Patch,
|
|
231
|
+
Delete,
|
|
232
|
+
Options,
|
|
233
|
+
Head,
|
|
234
|
+
All,
|
|
235
|
+
Ws,
|
|
236
|
+
Sse,
|
|
237
|
+
Body,
|
|
238
|
+
Param,
|
|
239
|
+
Query,
|
|
240
|
+
QueryParam,
|
|
241
|
+
Headers,
|
|
242
|
+
Req,
|
|
243
|
+
Res,
|
|
244
|
+
Next,
|
|
245
|
+
Ip,
|
|
246
|
+
Session,
|
|
247
|
+
UploadedFile,
|
|
248
|
+
UploadedFiles,
|
|
249
|
+
createParamDecorator,
|
|
250
|
+
Use,
|
|
251
|
+
Guards,
|
|
252
|
+
Filters,
|
|
253
|
+
Interceptors,
|
|
254
|
+
HttpCode,
|
|
255
|
+
Header,
|
|
256
|
+
Redirect,
|
|
257
|
+
SetMetadata,
|
|
258
|
+
Reflector,
|
|
259
|
+
} from 'exisjs/decorators'
|
|
248
260
|
```
|
|
249
261
|
|
|
250
262
|
---
|
|
251
263
|
|
|
252
|
-
|
|
264
|
+
### `@Controller(prefix?, options?)`
|
|
253
265
|
|
|
254
|
-
|
|
266
|
+
Decorates a class as an HTTP controller slice mounted at the specified route prefix.
|
|
255
267
|
|
|
256
|
-
|
|
268
|
+
```typescript
|
|
269
|
+
function Controller(
|
|
270
|
+
prefixOrOptions?: string | ControllerOptions,
|
|
271
|
+
options?: ControllerOptions
|
|
272
|
+
): ClassDecorator
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### Example
|
|
257
276
|
|
|
258
277
|
```typescript filename="src/http/users/route.ts"
|
|
259
278
|
import { Controller, Get, Post, Body, Param } from 'exisjs/decorators'
|
|
@@ -261,131 +280,94 @@ import { tex } from 'exisjs/validator'
|
|
|
261
280
|
|
|
262
281
|
@Controller('/users')
|
|
263
282
|
export default class UserController {
|
|
264
|
-
|
|
265
283
|
@Get('/:id')
|
|
266
284
|
async getUser(@Param('id') id: string) {
|
|
267
285
|
return { id, name: 'Alice' }
|
|
268
286
|
}
|
|
269
287
|
|
|
270
288
|
@Post('/', {
|
|
271
|
-
body: tex.object({
|
|
289
|
+
body: tex.object({
|
|
290
|
+
name: tex.string(),
|
|
291
|
+
email: tex.email(),
|
|
292
|
+
}),
|
|
272
293
|
})
|
|
273
|
-
async createUser(@Body() body:
|
|
274
|
-
|
|
275
|
-
return { success: true, email: body.email }
|
|
294
|
+
async createUser(@Body() body: { name: string; email: string }) {
|
|
295
|
+
return { success: true, user: body }
|
|
276
296
|
}
|
|
277
297
|
}
|
|
278
298
|
```
|
|
279
299
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
- `@Get(path, schema?)`
|
|
283
|
-
- `@Post(path, schema?)`
|
|
284
|
-
- `@Put(path, schema?)`
|
|
285
|
-
- `@Patch(path, schema?)`
|
|
286
|
-
- `@Delete(path, schema?)`
|
|
287
|
-
- `@Ws(path, schema?)`: Defines a WebSocket route.
|
|
288
|
-
- `@Sse(path, schema?)`: Defines a Server-Sent Events stream route.
|
|
289
|
-
|
|
290
|
-
*Note: Just like functional routes, you can pass an optional `RouteSchema` to any of these decorators to automatically validate requests!*
|
|
291
|
-
|
|
292
|
-
### Parameter Injectors
|
|
293
|
-
|
|
294
|
-
Use parameter decorators to automatically extract data from the incoming request and inject them into your method arguments:
|
|
295
|
-
|
|
296
|
-
- `@Body()`: Injects the parsed JSON request body.
|
|
297
|
-
- `@Param('id')`: Injects a specific URL path parameter.
|
|
298
|
-
- `@QueryParam('search')` / `@Query('search')`: Injects a specific query string parameter.
|
|
299
|
-
- `@Headers('authorization')`: Injects a specific HTTP header.
|
|
300
|
-
- `@Req()`: Injects the raw ExisJS Request object.
|
|
301
|
-
- `@Res()`: Injects the raw ExisJS Response object.
|
|
302
|
-
- `@Next()`: Injects the next function.
|
|
303
|
-
- `@Socket()`: Injects the active WebSocket instance (for `@Ws` routes).
|
|
304
|
-
- `@Stream()`: Injects the active SSE instance (for `@Sse` routes).
|
|
305
|
-
- `createParamDecorator(factory)`: Factory to construct custom parameter decorators.
|
|
306
|
-
|
|
307
|
-
### Extended Decorators
|
|
308
|
-
|
|
309
|
-
ExisJS provides a rich ecosystem of decorators to apply middleware, configure responses, and enforce security on both individual routes and entire controllers:
|
|
300
|
+
---
|
|
310
301
|
|
|
311
|
-
|
|
312
|
-
- `@Use(...items)`: Unified decorator that applies middlewares, Guards (`canActivate`), Interceptors (`intercept`), and Exception Filters (`catch`) to a route or entire controller.
|
|
313
|
-
- `@Hosts('api.example.com')`: Restricts the route or controller to specific domains.
|
|
302
|
+
### HTTP Method Decorators
|
|
314
303
|
|
|
315
|
-
|
|
316
|
-
- `@Permissions('admin')`: Requires specific permission flags.
|
|
317
|
-
- `@SetMetadata(key, value)`: Attaches arbitrary metadata to classes and handler methods (retrieved via `Reflector`).
|
|
318
|
-
- `Reflector`: Reads and merges metadata attached with `@SetMetadata`.
|
|
304
|
+
Method decorators attach HTTP handler logic and validation schemas to controller methods:
|
|
319
305
|
|
|
320
|
-
|
|
321
|
-
- `@
|
|
322
|
-
- `@
|
|
323
|
-
- `@
|
|
324
|
-
- `@
|
|
306
|
+
- `@Get(path?, schema?)`
|
|
307
|
+
- `@Post(path?, schema?)`
|
|
308
|
+
- `@Put(path?, schema?)`
|
|
309
|
+
- `@Patch(path?, schema?)`
|
|
310
|
+
- `@Delete(path?, schema?)`
|
|
311
|
+
- `@Options(path?, schema?)`
|
|
312
|
+
- `@Head(path?, schema?)`
|
|
313
|
+
- `@All(path?, schema?)`
|
|
314
|
+
- `@Sse(path?, schema?)`
|
|
315
|
+
- `@Ws(path?, schema?)`
|
|
325
316
|
|
|
326
317
|
---
|
|
327
318
|
|
|
328
|
-
|
|
319
|
+
### Parameter Injector Decorators
|
|
329
320
|
|
|
330
|
-
|
|
321
|
+
Parameter decorators extract and inject typed data directly into handler method parameters:
|
|
331
322
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
323
|
+
| Decorator | Injected Data | Example |
|
|
324
|
+
| :--- | :--- | :--- |
|
|
325
|
+
| `@Body(property?)` | Request body payload or sub-property | `@Body() data: CreateUserDto` |
|
|
326
|
+
| `@Param(key?)` | URL path parameters dictionary or specific param | `@Param('id') userId: string` |
|
|
327
|
+
| `@Query(key?)` / `@QueryParam(key?)` | Query string parameters dictionary or specific key | `@Query('limit') limit: number` |
|
|
328
|
+
| `@Headers(name?)` | Request headers dictionary or specific header | `@Headers('authorization') auth: string` |
|
|
329
|
+
| `@Req()` | Underlying `ExisRequest` instance | `@Req() req: Request` |
|
|
330
|
+
| `@Res()` | Underlying `ExisResponse` instance | `@Res() res: Response` |
|
|
331
|
+
| `@Next()` | Pipeline `NextFunction` callback | `@Next() next: NextFunction` |
|
|
332
|
+
| `@Ip()` | Client remote IP address | `@Ip() clientIp: string` |
|
|
333
|
+
| `@UploadedFile(field?)` | Single uploaded multipart file object | `@UploadedFile('avatar') file: ExisFile` |
|
|
334
|
+
| `@UploadedFiles(field?)` | Multiple uploaded multipart file objects | `@UploadedFiles('docs') files: ExisFile[]` |
|
|
335
335
|
|
|
336
336
|
---
|
|
337
337
|
|
|
338
|
-
|
|
338
|
+
### `createParamDecorator(factory)`
|
|
339
339
|
|
|
340
|
-
|
|
340
|
+
Constructs custom parameter decorators to extract specialized values from the request context:
|
|
341
341
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
Place a `boundary.ts` file in any route directory (e.g., `src/http/boundary.ts` or `src/http/api/v1/boundary.ts`):
|
|
345
|
-
|
|
346
|
-
```typescript filename="src/http/api/boundary.ts"
|
|
347
|
-
import { defineBoundary } from 'exisjs/router'
|
|
348
|
-
import { authGuard } from '../../guards/auth'
|
|
342
|
+
```typescript
|
|
343
|
+
import { createParamDecorator } from 'exisjs/decorators'
|
|
349
344
|
|
|
350
|
-
export
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
origin: 'https://app.example.com',
|
|
354
|
-
credentials: true,
|
|
355
|
-
},
|
|
356
|
-
guards: [authGuard],
|
|
357
|
-
headers: {
|
|
358
|
-
'X-API-Version': 'v1',
|
|
359
|
-
},
|
|
360
|
-
// Exclude public endpoints from this boundary's pipeline
|
|
361
|
-
exclude: ['/api/health', '/api/public/*'],
|
|
345
|
+
export const CurrentUser = createParamDecorator((data: string | undefined, ctx) => {
|
|
346
|
+
const req = ctx.req
|
|
347
|
+
return data ? req.user?.[data] : req.user
|
|
362
348
|
})
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### Cascading & Precedence Rules
|
|
366
349
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
- **Child Boundary `cors`** (in `src/http/api/boundary.ts`) overrides parent boundary `cors` (in `src/http/boundary.ts`).
|
|
374
|
-
- **Parent Boundary `cors`** applies if no child boundary or route overrides it.
|
|
375
|
-
- **Global `app.options.cors`** acts as the root fallback.
|
|
376
|
-
3. **Response Headers (Deep Merge with Child Precedence)**:
|
|
377
|
-
- Headers defined in child boundaries or route definitions overwrite identical header keys from parent boundaries.
|
|
350
|
+
// Usage in controller:
|
|
351
|
+
@Get('/profile')
|
|
352
|
+
getProfile(@CurrentUser() user: any, @CurrentUser('id') userId: string) {
|
|
353
|
+
return { userId, user }
|
|
354
|
+
}
|
|
355
|
+
```
|
|
378
356
|
|
|
379
357
|
---
|
|
380
358
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
359
|
+
### Pipeline & Response Decorators
|
|
360
|
+
|
|
361
|
+
| Decorator | Target | Description |
|
|
362
|
+
| :--- | :--- | :--- |
|
|
363
|
+
| `@Use(...middlewares)` | Class / Method | Applies middleware functions to the controller or route. |
|
|
364
|
+
| `@Guards(...guards)` | Class / Method | Attaches authorization guards. |
|
|
365
|
+
| `@Filters(...filters)` | Class / Method | Attaches exception filters. |
|
|
366
|
+
| `@Interceptors(...interceptors)` | Class / Method | Attaches response/request interceptors. |
|
|
367
|
+
| `@HttpCode(status)` | Method | Sets the default HTTP status code for successful responses. |
|
|
368
|
+
| `@Header(name, value)` | Method | Sets static HTTP response headers. |
|
|
369
|
+
| `@Redirect(url, status?)` | Method | Redirects responses to the target URL. |
|
|
370
|
+
| `@SetMetadata(key, value)` | Class / Method | Attaches arbitrary metadata readable via `Reflector`. |
|
|
389
371
|
|
|
390
372
|
|
|
391
373
|
|