exisjs 0.7.11 → 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/cli/commands/dev.d.ts +4 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +136 -32
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/run.d.ts +7 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +99 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/index.js +29 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/manifest.d.ts.map +1 -1
- package/dist/cli/manifest.js +18 -3
- package/dist/cli/manifest.js.map +1 -1
- package/dist/cli/routes.d.ts +6 -0
- package/dist/cli/routes.d.ts.map +1 -1
- package/dist/cli/routes.js +224 -135
- package/dist/cli/routes.js.map +1 -1
- package/dist/config/config.d.ts +2 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +30 -9
- package/dist/config/types.d.ts.map +1 -1
- package/dist/database/lifecycle.d.ts +6 -1
- package/dist/database/lifecycle.d.ts.map +1 -1
- package/dist/database/lifecycle.js +15 -4
- package/dist/database/lifecycle.js.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 +141 -48
- package/dist/docs/content/cli/commands.mdx +161 -107
- package/dist/docs/content/cli/overview.mdx +61 -46
- package/dist/docs/content/cli/usage.mdx +107 -30
- package/dist/docs/content/config.mdx +145 -147
- 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 +239 -558
- 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 -73
- 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 +277 -251
- 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/inspect-routes.d.ts +2 -0
- package/dist/lib/inspect-routes.d.ts.map +1 -0
- package/dist/lib/inspect-routes.js +148 -0
- package/dist/lib/inspect-routes.js.map +1 -0
- package/dist/lib/start-server.js +13 -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/router/scanner/cron-mounter.d.ts.map +1 -1
- package/dist/router/scanner/cron-mounter.js +9 -2
- package/dist/router/scanner/cron-mounter.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 +29 -8
- package/dist/server/app.js.map +1 -1
- package/dist/server/define.d.ts.map +1 -1
- package/dist/server/define.js +5 -0
- package/dist/server/define.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 +20 -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 +47 -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)`
|
|
51
|
-
|
|
52
|
-
Merges multiple functional controllers into a single unified controller definition. This enables splitting large `route.ts` files into modular, domain-specific controller pieces while exporting a single composite controller from the route file.
|
|
49
|
+
### `controller(config)`
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
import { controller, route } from 'exisjs/router'
|
|
51
|
+
Wraps route definitions, file-scoped middlewares, guards, and CORS policies into an exportable controller slice.
|
|
56
52
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
193
|
+
### `defineBoundary(config)`
|
|
210
194
|
|
|
211
|
-
|
|
195
|
+
Declares hierarchical folder-level middleware, security controls, and response defaults inside `boundary.ts` files.
|
|
212
196
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
```
|
|
218
|
-
import { createRouter } from "exisjs/router";
|
|
197
|
+
```typescript
|
|
198
|
+
function defineBoundary<TContext = Record<string, any>>(
|
|
199
|
+
config: BoundaryConfig<TContext>
|
|
200
|
+
): BoundaryConfig<TContext>
|
|
201
|
+
```
|
|
219
202
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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. |
|
|
225
217
|
|
|
226
|
-
|
|
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,87 +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.
|
|
300
|
+
---
|
|
289
301
|
|
|
290
|
-
|
|
302
|
+
### HTTP Method Decorators
|
|
291
303
|
|
|
292
|
-
|
|
304
|
+
Method decorators attach HTTP handler logic and validation schemas to controller methods:
|
|
293
305
|
|
|
294
|
-
|
|
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?)`
|
|
295
316
|
|
|
296
|
-
|
|
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.
|
|
317
|
+
---
|
|
306
318
|
|
|
307
|
-
###
|
|
319
|
+
### Parameter Injector Decorators
|
|
308
320
|
|
|
309
|
-
|
|
321
|
+
Parameter decorators extract and inject typed data directly into handler method parameters:
|
|
310
322
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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[]` |
|
|
314
335
|
|
|
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`.
|
|
336
|
+
---
|
|
319
337
|
|
|
320
|
-
|
|
321
|
-
- `@HttpCode(201)`: Forces a specific HTTP status code for successful responses.
|
|
322
|
-
- `@Header('X-Custom', 'Value')`: Automatically injects a specific response header.
|
|
323
|
-
- `@Returns(schema)`: Explicitly defines the response schema for OpenAPI generation.
|
|
324
|
-
- `@Idempotent()`: Enforces idempotency keys for mutations (e.g., checkout/payments).
|
|
338
|
+
### `createParamDecorator(factory)`
|
|
325
339
|
|
|
326
|
-
|
|
340
|
+
Constructs custom parameter decorators to extract specialized values from the request context:
|
|
327
341
|
|
|
328
|
-
|
|
342
|
+
```typescript
|
|
343
|
+
import { createParamDecorator } from 'exisjs/decorators'
|
|
329
344
|
|
|
330
|
-
|
|
345
|
+
export const CurrentUser = createParamDecorator((data: string | undefined, ctx) => {
|
|
346
|
+
const req = ctx.req
|
|
347
|
+
return data ? req.user?.[data] : req.user
|
|
348
|
+
})
|
|
331
349
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
350
|
+
// Usage in controller:
|
|
351
|
+
@Get('/profile')
|
|
352
|
+
getProfile(@CurrentUser() user: any, @CurrentUser('id') userId: string) {
|
|
353
|
+
return { userId, user }
|
|
354
|
+
}
|
|
355
|
+
```
|
|
335
356
|
|
|
336
357
|
---
|
|
337
358
|
|
|
338
|
-
|
|
359
|
+
### Pipeline & Response Decorators
|
|
339
360
|
|
|
340
|
-
|
|
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`. |
|
|
341
371
|
|
|
342
|
-
- `AppRouterPaths`: Flat dictionary mapping URL route patterns (e.g. `'/users/:id'`) to their strongly-typed handler contracts (`(payload?: B, options?: { query?: Q; params?: P }) => Promise<R>`).
|
|
343
|
-
- `AppRouterTree`: Hierarchical dot-notation tree mapping nested folder paths (`users: { ':id': ... }`) for `@exisjs/fetch`'s `createClient<AppRouter>()`.
|
|
344
|
-
- `AppRouter`: Combined intersection type `AppRouterPaths & AppRouterTree`.
|
|
345
|
-
- `RouteDefinition.__type`: Phantom type property on every route definition preserving exact inferred return types, DTO schemas, and Drizzle/Prisma models.
|
|
346
372
|
|
|
347
373
|
|