exisjs 0.7.12 → 0.7.14
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/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +3 -1
- package/dist/cli/commands/run.js.map +1 -1
- 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/ARCHITECTURE.md +130 -78
- 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 +22 -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/fs-scanner.d.ts.map +1 -1
- package/dist/router/scanner/fs-scanner.js +22 -0
- package/dist/router/scanner/fs-scanner.js.map +1 -1
- package/dist/router/types.d.ts +6 -4
- package/dist/router/types.d.ts.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/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +19 -0
- package/dist/server/bootstrapper.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/server/response.d.ts +10 -5
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +65 -18
- package/dist/server/response.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 +19 -4
- 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,286 +1,241 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
title: 'Project Structure'
|
|
3
|
+
description: 'Directory conventions, file naming rules, auto-discovery mechanisms, and structural architecture for ExisJS applications.'
|
|
4
|
+
nextUrl: '/docs/routing'
|
|
5
|
+
nextTitle: 'Routing & Endpoints'
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# Project Structure
|
|
7
9
|
|
|
8
|
-
ExisJS
|
|
9
|
-
validation, sanitization, and a request pipeline. Everything else — databases,
|
|
10
|
-
auth, caching, queues, websockets — is an official `@exisjs/*` package you add
|
|
11
|
-
only when you need it. Nothing in this structure requires them.
|
|
12
|
-
|
|
13
|
-
ExisJS enforces a strict `src/` directory convention. All application code
|
|
14
|
-
**must** live inside `src/`.
|
|
10
|
+
ExisJS enforces a convention-based project structure located inside the `src/` directory. File and folder locations declare their roles automatically, removing the need for manual routing configuration or boilerplate router files.
|
|
15
11
|
|
|
16
12
|
---
|
|
17
13
|
|
|
18
|
-
##
|
|
14
|
+
## Standard Directory Layout
|
|
19
15
|
|
|
20
16
|
```text
|
|
21
|
-
my-
|
|
22
|
-
├── .
|
|
17
|
+
my-api/
|
|
18
|
+
├── .agents/
|
|
19
|
+
│ └── rules/
|
|
20
|
+
│ └── AGENTS.md # LLM architectural rules and coding standards
|
|
21
|
+
├── .exis/ # Framework cache and compiled route manifests (git-ignored)
|
|
23
22
|
├── src/
|
|
24
|
-
│ ├── http/
|
|
25
|
-
│ │ ├── server.ts
|
|
26
|
-
│ │ ├──
|
|
27
|
-
│ │ ├── route.ts
|
|
28
|
-
│ │ ├──
|
|
23
|
+
│ ├── http/ # File-system routing and HTTP request pipeline (auto-discovered)
|
|
24
|
+
│ │ ├── server.ts # Server lifecycle hooks (onStart, onClose)
|
|
25
|
+
│ │ ├── boundary.ts # Root boundary (CORS, security headers, global error handling)
|
|
26
|
+
│ │ ├── route.ts # Root route handler (GET /)
|
|
27
|
+
│ │ ├── error.ts # Global exception and validation error handler (onError)
|
|
28
|
+
│ │ ├── health/
|
|
29
|
+
│ │ │ └── route.ts # GET /health endpoint
|
|
29
30
|
│ │ ├── users/
|
|
30
|
-
│ │ │ ├── route.ts
|
|
31
|
-
│ │ │ ├── schema.ts
|
|
32
|
-
│ │ │ └── service.ts
|
|
33
|
-
│ │ ├── posts/
|
|
34
|
-
│ │ │ ├── route.ts
|
|
35
|
-
│ │ │ ├── schema.ts
|
|
36
|
-
│ │ │ └── service.ts
|
|
31
|
+
│ │ │ ├── route.ts # Route endpoints for /users
|
|
32
|
+
│ │ │ ├── schema.ts # Input validation schemas (tex.*)
|
|
33
|
+
│ │ │ └── service.ts # Feature business logic and data access
|
|
37
34
|
│ │ └── admin/
|
|
38
|
-
│ │ ├── boundary.ts
|
|
39
|
-
│ │ ├── route.ts
|
|
35
|
+
│ │ ├── boundary.ts # Scoped boundary applying to /admin and nested routes
|
|
40
36
|
│ │ └── posts/
|
|
41
|
-
│ │ ├── route.ts
|
|
42
|
-
│ │ ├── schema.ts
|
|
43
|
-
│ │ └── service.ts
|
|
44
|
-
│ ├── cron/
|
|
45
|
-
│ │ └── cleanup.ts
|
|
46
|
-
│ ├── database/
|
|
47
|
-
│ │ ├── db.ts
|
|
48
|
-
│ │ └── models/
|
|
49
|
-
│ ├── common/
|
|
37
|
+
│ │ ├── route.ts # Route endpoints for /admin/posts
|
|
38
|
+
│ │ ├── schema.ts # Validation schemas for /admin/posts
|
|
39
|
+
│ │ └── service.ts # Service logic for /admin/posts
|
|
40
|
+
│ ├── cron/ # Scheduled background tasks (auto-discovered)
|
|
41
|
+
│ │ └── cleanup.ts # Cron task definitions
|
|
42
|
+
│ ├── database/ # Persistent storage configuration and lifecycle
|
|
43
|
+
│ │ ├── db.ts # Database connection client and lifecycle registration
|
|
44
|
+
│ │ └── models/ # Database models, schemas, and entity definitions
|
|
45
|
+
│ ├── common/ # Shared cross-cutting components
|
|
50
46
|
│ │ └── guards/
|
|
51
|
-
│ │ └── auth.guard.ts
|
|
47
|
+
│ │ └── auth.guard.ts # Reusable route guards
|
|
52
48
|
│ └── config/
|
|
53
|
-
│ └── env.ts
|
|
54
|
-
├── tests/
|
|
55
|
-
├── .env
|
|
56
|
-
├──
|
|
49
|
+
│ └── env.ts # Environment variable validation schema (tex.env)
|
|
50
|
+
├── tests/ # Automated test suites (*.test.ts, *.e2e-spec.ts)
|
|
51
|
+
├── .env # Local environment variables
|
|
52
|
+
├── .gitignore
|
|
53
|
+
├── .prettierrc
|
|
54
|
+
├── eslint.config.mjs # ESLint flat configuration (optional)
|
|
55
|
+
├── exis.config.ts # Application configuration file
|
|
57
56
|
├── package.json
|
|
58
57
|
└── tsconfig.json
|
|
59
58
|
```
|
|
60
59
|
|
|
61
|
-
> `src/cron/` is natively auto-discovered by the framework at boot. All files inside `src/cron/` or colocated at `src/http/**/cron.ts` are automatically mounted. Database connections and models reside under `src/database/` (or `src/models/`).
|
|
62
|
-
|
|
63
60
|
---
|
|
64
61
|
|
|
65
|
-
## File
|
|
62
|
+
## File Conventions & Responsibilities
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
_syntax_, never _responsibility_.
|
|
64
|
+
Within `src/http/`, each file name denotes a specific role in the HTTP lifecycle:
|
|
69
65
|
|
|
70
|
-
|
|
66
|
+
| File Pattern | Role | Description |
|
|
67
|
+
| :--- | :--- | :--- |
|
|
68
|
+
| `route.ts` / `*.route.ts` | Endpoint Definition | Binds HTTP methods (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) to URL paths. |
|
|
69
|
+
| `schema.ts` / `*.schema.ts` | Request Validation | Defines `tex` schemas for request body, query parameters, URL path parameters, and headers. |
|
|
70
|
+
| `service.ts` / `*.service.ts` | Business Logic | Handles database operations and domain logic. Injected via Dependency Injection or called as pure functions. |
|
|
71
|
+
| `boundary.ts` / `*.boundary.ts` | Scoped Pipeline | Folder-level configuration (CORS, headers, rate limits, authentication guards, and error handling) cascading down directory trees. |
|
|
72
|
+
| `server.ts` | Server Lifecycle | Exports `onStart` and `onClose` hooks for database connections and plugin initialization. |
|
|
73
|
+
| `error.ts` / `*.error.ts` | Exception Handling | Centralized error interceptor exporting `onError(err, req, res)`. |
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
2. **Feature-Prefixed Named Files**: `<name>.route.ts`, `<name>.schema.ts`, `<name>.service.ts`, `<name>.boundary.ts` (e.g., `user.route.ts`, `user.schema.ts`, `user.service.ts`).
|
|
75
|
+
---
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
## Directory Details
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
| :------------------------------ | :--------------------------------------------------------------------------------------------- | :------------------------------- |
|
|
79
|
-
| `route.ts` / `*.route.ts` | Binds a URL + method to a handler. | Business logic, validation logic |
|
|
80
|
-
| `schema.ts` / `*.schema.ts` | `tex` validation schemas, imported by route files. Mandatory for every route accepting input. | Handlers, routing |
|
|
81
|
-
| `service.ts` / `*.service.ts` | Business logic and data access. DI-injectable. | Routing, response shaping |
|
|
82
|
-
| `boundary.ts` / `*.boundary.ts` | Folder-scoped config + request pipeline for everything in this folder and below. | — |
|
|
83
|
-
| `error.ts` / `*.error.ts` | Centralized exception and validation error handler (`onError`). Auto-mounted by the framework. | Business logic, routes |
|
|
79
|
+
### 1. `src/http/` (File-System Routing)
|
|
84
80
|
|
|
85
|
-
|
|
81
|
+
Every folder inside `src/http/` maps directly to an HTTP endpoint path:
|
|
86
82
|
|
|
87
|
-
|
|
83
|
+
- `src/http/route.ts` → `/`
|
|
84
|
+
- `src/http/users/route.ts` → `/users`
|
|
85
|
+
- `src/http/users/[id]/route.ts` → `/users/:id`
|
|
86
|
+
- `src/http/(admin)/dashboard/route.ts` → `/dashboard` (parenthesized route groups do not affect the URL path)
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
#### Paradigm Examples
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
- `src/http/users/route.ts` → `/api/users`
|
|
93
|
-
- `src/http/users/[id]/route.ts` → `/api/users/:id`
|
|
94
|
-
- `src/http/(internal)/debug/route.ts` → `/api/debug` (group folders are ignored)
|
|
95
|
-
|
|
96
|
-
### Functional
|
|
90
|
+
##### Functional Paradigm
|
|
97
91
|
|
|
98
92
|
```typescript filename="src/http/users/route.ts"
|
|
99
93
|
import { controller, route } from 'exisjs/router'
|
|
100
|
-
import {
|
|
101
|
-
import {
|
|
94
|
+
import { userParamsSchema } from './schema'
|
|
95
|
+
import { getUserById } from './service'
|
|
102
96
|
|
|
103
97
|
export default controller({
|
|
104
|
-
getUser: route.get('
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
getUser: route.get('/:id', {
|
|
99
|
+
summary: 'Retrieve user by ID',
|
|
100
|
+
params: userParamsSchema,
|
|
101
|
+
handle: async ({ params }) => {
|
|
102
|
+
return getUserById(params.id)
|
|
108
103
|
},
|
|
109
104
|
}),
|
|
110
105
|
})
|
|
111
106
|
```
|
|
112
107
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
The decorated class **is** the route file — no separate controller file:
|
|
108
|
+
##### Class-Based (OOP) Paradigm
|
|
116
109
|
|
|
117
110
|
```typescript filename="src/http/users/route.ts"
|
|
118
|
-
import { Controller, Get, Param } from 'exisjs/decorators'
|
|
119
|
-
import {
|
|
111
|
+
import { Controller, Get, Param, ApiOperation } from 'exisjs/decorators'
|
|
112
|
+
import { userParamsSchema } from './schema'
|
|
120
113
|
import { UserService } from './service'
|
|
121
114
|
|
|
122
|
-
@Controller(
|
|
123
|
-
export default class
|
|
124
|
-
constructor(private userService: UserService) {}
|
|
115
|
+
@Controller()
|
|
116
|
+
export default class UsersController {
|
|
117
|
+
constructor(private readonly userService: UserService) {}
|
|
125
118
|
|
|
126
|
-
@Get('/:id',
|
|
119
|
+
@Get('/:id', { params: userParamsSchema })
|
|
120
|
+
@ApiOperation({ summary: 'Retrieve user by ID' })
|
|
127
121
|
async getUser(@Param('id') id: string) {
|
|
128
|
-
return this.userService.
|
|
122
|
+
return this.userService.getUserById(id)
|
|
129
123
|
}
|
|
130
124
|
}
|
|
131
125
|
```
|
|
132
126
|
|
|
133
|
-
> [!IMPORTANT]
|
|
134
|
-
> Choose **one paradigm** — Functional or OOP — for the entire project.
|
|
135
|
-
> Mixing them causes a fatal error at startup.
|
|
136
|
-
|
|
137
127
|
---
|
|
138
128
|
|
|
139
|
-
|
|
129
|
+
### 2. `src/http/**/schema.ts` (Validation Schemas)
|
|
130
|
+
|
|
131
|
+
Define request payload and parameter schemas using the `tex` schema builder. Schemas defined here provide compile-time type inference and drive the OpenAPI documentation generator:
|
|
140
132
|
|
|
141
133
|
```typescript filename="src/http/users/schema.ts"
|
|
142
|
-
import { tex } from 'exisjs/validator'
|
|
134
|
+
import { tex, type ResolveSchema } from 'exisjs/validator'
|
|
143
135
|
|
|
144
|
-
export const
|
|
145
|
-
|
|
136
|
+
export const userParamsSchema = tex.object({
|
|
137
|
+
id: tex.string({ min: 1 }),
|
|
138
|
+
})
|
|
146
139
|
|
|
147
|
-
|
|
148
|
-
|
|
140
|
+
export const createUserSchema = tex.object({
|
|
141
|
+
name: tex.string({ min: 2 }),
|
|
142
|
+
email: tex.email(),
|
|
143
|
+
role: tex.enum(['admin', 'member'], { default: 'member' }),
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
export type CreateUserDto = ResolveSchema<typeof createUserSchema>
|
|
147
|
+
export type UserParamsDto = ResolveSchema<typeof userParamsSchema>
|
|
148
|
+
```
|
|
149
149
|
|
|
150
150
|
---
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
### 3. `src/http/**/service.ts` (Business Logic)
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
Isolate database interactions and business logic from HTTP transport concerns:
|
|
155
|
+
|
|
156
|
+
#### Functional Service
|
|
156
157
|
|
|
157
158
|
```typescript filename="src/http/users/service.ts"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// ...
|
|
159
|
+
export async function getUserById(id: string) {
|
|
160
|
+
return { id, name: 'Alice', role: 'member' }
|
|
161
161
|
}
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
#### Class-Based (OOP) Service
|
|
165
|
+
|
|
164
166
|
```typescript filename="src/http/users/service.ts"
|
|
165
|
-
// OOP
|
|
166
167
|
import { Injectable } from 'exisjs/decorators'
|
|
167
168
|
|
|
168
169
|
@Injectable()
|
|
169
170
|
export class UserService {
|
|
170
|
-
async
|
|
171
|
-
|
|
171
|
+
async getUserById(id: string) {
|
|
172
|
+
return { id, name: 'Alice', role: 'member' }
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
```
|
|
175
176
|
|
|
176
177
|
---
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
A `boundary.ts` applies to every route in its folder and all subfolders. It has
|
|
181
|
-
two independent halves:
|
|
182
|
-
|
|
183
|
-
1. **Config** — pure data, via `defineBoundary()` (functional) or `@Boundary()`
|
|
184
|
-
(OOP argument). CORS, headers, DI providers, excluded paths, and lifecycle hooks.
|
|
185
|
-
2. **Pipeline & Hooks** — real code, auto-detected by function signature. No imports,
|
|
186
|
-
no manual registration required.
|
|
179
|
+
### 4. `src/http/**/boundary.ts` (Hierarchical Boundaries)
|
|
187
180
|
|
|
188
|
-
|
|
189
|
-
| :----------------------------------------------------------------------- | :------------------ | :--------------------------------------------------------------------------------- |
|
|
190
|
-
| `(req, res, next)` | Chain Step | In file/declaration order, before the route handler |
|
|
191
|
-
| `beforeHandle(req, res)` | Boundary Guard Hook | Immediately before route execution. Short-circuits on `false` or when headers sent |
|
|
192
|
-
| `afterHandle(req, res, data)` | Response Hook | Immediately after route execution. Receives returned data to transform or audit |
|
|
193
|
-
| `(ctx, next)` — default export (functional) or `handle(ctx, next)` (OOP) | Pipeline Wrapper | Around all chain steps and the nested route/boundary |
|
|
181
|
+
A `boundary.ts` file applies scoped security guards, CORS settings, custom headers, and error handling to all routes within its directory and subdirectories:
|
|
194
182
|
|
|
195
|
-
|
|
183
|
+
#### Functional Boundary
|
|
196
184
|
|
|
197
185
|
```typescript filename="src/http/admin/boundary.ts"
|
|
198
186
|
import { defineBoundary } from 'exisjs/router'
|
|
199
|
-
import type { Request, Response, Next
|
|
187
|
+
import type { Request, Response, Next } from 'exisjs/router'
|
|
200
188
|
|
|
201
189
|
export const config = defineBoundary({
|
|
202
|
-
cors: { origin: ['https://
|
|
203
|
-
headers: { 'X-
|
|
204
|
-
exclude: [
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
res.status(403).json({ error: 'Admin role required' })
|
|
190
|
+
cors: { origin: ['https://admin.example.com'], credentials: true },
|
|
191
|
+
headers: { 'X-Admin-Scope': 'active' },
|
|
192
|
+
exclude: ['/admin/health'],
|
|
193
|
+
|
|
194
|
+
beforeHandle(req: Request, res: Response) {
|
|
195
|
+
if (req.headers['x-admin-key'] !== process.env.ADMIN_KEY) {
|
|
196
|
+
res.status(403).json({ error: 'Unauthorized admin access' })
|
|
210
197
|
return false
|
|
211
198
|
}
|
|
212
199
|
},
|
|
213
|
-
|
|
214
|
-
// Post-handler hook (transforms returned payload or performs async audit)
|
|
215
|
-
afterHandle(req, res, data) {
|
|
216
|
-
res.setHeader('X-Audited-By', 'AdminBoundary')
|
|
217
|
-
return { ...data, auditedAt: Date.now() }
|
|
218
|
-
},
|
|
219
200
|
})
|
|
220
201
|
|
|
221
|
-
export function
|
|
222
|
-
console.log(`[Admin] ${req.method} ${req.path}`)
|
|
202
|
+
export function logAdminAccess(req: Request, res: Response, next: Next) {
|
|
203
|
+
console.log(`[Admin Access] ${req.method} ${req.path}`)
|
|
223
204
|
next()
|
|
224
205
|
}
|
|
225
|
-
|
|
226
|
-
export default async function (ctx: BoundaryContext, next: Next) {
|
|
227
|
-
try {
|
|
228
|
-
return { success: true, data: await next() }
|
|
229
|
-
} catch (err) {
|
|
230
|
-
return { success: false, error: { message: (err as Error).message } }
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
206
|
```
|
|
234
207
|
|
|
235
|
-
|
|
208
|
+
#### Class-Based (OOP) Boundary
|
|
236
209
|
|
|
237
210
|
```typescript filename="src/http/admin/boundary.ts"
|
|
238
211
|
import { Boundary } from 'exisjs/decorators'
|
|
239
|
-
import type { Request, Response, Next
|
|
212
|
+
import type { Request, Response, Next } from 'exisjs/router'
|
|
240
213
|
|
|
241
214
|
@Boundary({
|
|
242
|
-
cors: { origin: ['https://
|
|
243
|
-
headers: { 'X-
|
|
215
|
+
cors: { origin: ['https://admin.example.com'], credentials: true },
|
|
216
|
+
headers: { 'X-Admin-Scope': 'active' },
|
|
244
217
|
exclude: ['/admin/health'],
|
|
245
218
|
})
|
|
246
219
|
export default class AdminBoundary {
|
|
247
|
-
// Pre-handler hook
|
|
248
220
|
beforeHandle(req: Request, res: Response) {
|
|
249
|
-
if (req.headers['x-
|
|
250
|
-
res.status(403).json({ error: '
|
|
221
|
+
if (req.headers['x-admin-key'] !== process.env.ADMIN_KEY) {
|
|
222
|
+
res.status(403).json({ error: 'Unauthorized admin access' })
|
|
251
223
|
return false
|
|
252
224
|
}
|
|
253
225
|
}
|
|
254
226
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return { ...data, auditedAt: Date.now() }
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Chain step
|
|
261
|
-
logStep(req: Request, res: Response, next: Next) {
|
|
262
|
-
console.log(`[Admin] ${req.method} ${req.path}`)
|
|
227
|
+
logAdminAccess(req: Request, res: Response, next: Next) {
|
|
228
|
+
console.log(`[Admin Access] ${req.method} ${req.path}`)
|
|
263
229
|
next()
|
|
264
230
|
}
|
|
265
|
-
|
|
266
|
-
// Wrapper
|
|
267
|
-
async handle(ctx: BoundaryContext, next: Next) {
|
|
268
|
-
try {
|
|
269
|
-
return { success: true, data: await next() }
|
|
270
|
-
} catch (err) {
|
|
271
|
-
return { success: false, error: { message: (err as Error).message } }
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
231
|
}
|
|
275
232
|
```
|
|
276
233
|
|
|
277
234
|
---
|
|
278
235
|
|
|
279
|
-
|
|
236
|
+
### 5. `src/http/error.ts` (Global Error Handler)
|
|
280
237
|
|
|
281
|
-
ExisJS
|
|
282
|
-
|
|
283
|
-
### Functional
|
|
238
|
+
ExisJS discovers `src/http/error.ts` to format application errors and validation failures consistently across all endpoints:
|
|
284
239
|
|
|
285
240
|
```typescript filename="src/http/error.ts"
|
|
286
241
|
import type { Request, Response } from 'exisjs'
|
|
@@ -288,7 +243,6 @@ import type { Request, Response } from 'exisjs'
|
|
|
288
243
|
export function onError(err: any, req: Request, res: Response) {
|
|
289
244
|
const status = err.statusCode || err.status || 500
|
|
290
245
|
|
|
291
|
-
// Format consistent error response across the entire API
|
|
292
246
|
res.status(status).json({
|
|
293
247
|
success: false,
|
|
294
248
|
error: {
|
|
@@ -300,50 +254,29 @@ export function onError(err: any, req: Request, res: Response) {
|
|
|
300
254
|
}
|
|
301
255
|
```
|
|
302
256
|
|
|
303
|
-
### Class-Based (OOP)
|
|
304
|
-
|
|
305
|
-
```typescript filename="src/http/error.ts"
|
|
306
|
-
import type { Request, Response } from 'exisjs'
|
|
307
|
-
|
|
308
|
-
export default class GlobalErrorHandler {
|
|
309
|
-
onError(err: any, req: Request, res: Response) {
|
|
310
|
-
const status = err.statusCode || err.status || 500
|
|
311
|
-
res.status(status).json({
|
|
312
|
-
success: false,
|
|
313
|
-
error: {
|
|
314
|
-
message: err.message || 'Internal Server Error',
|
|
315
|
-
code: err.code || 'INTERNAL_ERROR',
|
|
316
|
-
path: req.path,
|
|
317
|
-
},
|
|
318
|
-
})
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
A nested folder's `boundary.ts` always runs _inside_ its parent's `next()` —
|
|
324
|
-
pipeline depth mirrors folder depth automatically.
|
|
325
|
-
|
|
326
257
|
---
|
|
327
258
|
|
|
328
|
-
|
|
259
|
+
### 6. `src/cron/` (Scheduled Background Tasks)
|
|
329
260
|
|
|
330
|
-
|
|
331
|
-
official package registration.
|
|
261
|
+
Files placed inside `src/cron/` are automatically mounted by the scheduler during server boot:
|
|
332
262
|
|
|
333
|
-
```typescript filename="
|
|
334
|
-
import {
|
|
263
|
+
```typescript filename="src/cron/cleanup.ts"
|
|
264
|
+
import { cron } from 'exisjs/cron'
|
|
335
265
|
|
|
336
|
-
export default
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
266
|
+
export default cron('0 0 * * *', {
|
|
267
|
+
name: 'daily-database-cleanup',
|
|
268
|
+
run: async () => {
|
|
269
|
+
console.log('[Cron] Executing nightly database maintenance...')
|
|
270
|
+
},
|
|
341
271
|
})
|
|
342
272
|
```
|
|
343
273
|
|
|
344
274
|
---
|
|
345
275
|
|
|
346
|
-
|
|
276
|
+
### 7. `src/database/` & `src/config/`
|
|
277
|
+
|
|
278
|
+
- **`src/database/db.ts`**: Contains database connections and lifecycle registration via `registerDatabase`.
|
|
279
|
+
- **`src/config/env.ts`**: Validates environment variables at boot using `tex.env`:
|
|
347
280
|
|
|
348
281
|
```typescript filename="src/config/env.ts"
|
|
349
282
|
import { tex } from 'exisjs/validator'
|
|
@@ -359,46 +292,24 @@ export const env = tex
|
|
|
359
292
|
.parse(process.env)
|
|
360
293
|
```
|
|
361
294
|
|
|
362
|
-
Boot fails immediately with clear diagnostics if a required environment variable is missing or malformed.
|
|
363
|
-
|
|
364
|
-
---
|
|
365
|
-
|
|
366
|
-
## `src/database/`
|
|
367
|
-
|
|
368
|
-
The `src/database/` directory organizes your persistent storage layer:
|
|
369
|
-
|
|
370
|
-
- `src/database/db.ts`: Connection instance, client initialization, and lifecycle registration via `registerDatabase(app, { connect, disconnect, healthCheck })` (or ORMs like Prisma, Drizzle, Mongoose).
|
|
371
|
-
- `src/database/models/` (or `src/models/`): Data models, entity definitions, and database schemas.
|
|
372
|
-
- `src/database/repositories/`: Abstracted data access objects and custom query builders.
|
|
373
|
-
- `src/database/migrations/`: Database migration scripts.
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
## `.exis/`
|
|
378
|
-
|
|
379
|
-
Internal build cache — compiled route manifest, used for O(1) boot in
|
|
380
|
-
production. Add to `.gitignore`.
|
|
381
|
-
|
|
382
295
|
---
|
|
383
296
|
|
|
384
|
-
## Auto-Discovered vs
|
|
297
|
+
## Auto-Discovered vs Convention Directories
|
|
385
298
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
299
|
+
| Directory | Type | Description |
|
|
300
|
+
| :--- | :--- | :--- |
|
|
301
|
+
| `src/http/` | Auto-Discovered | Scanned at startup for routes, boundaries, schemas, services, and error handlers. |
|
|
302
|
+
| `src/cron/` | Auto-Discovered | Scanned at startup for scheduled background task definitions. |
|
|
303
|
+
| `src/database/` | Convention | Recommended folder for database connection instances, ORM models, and migrations. |
|
|
304
|
+
| `src/common/` | Convention | Recommended folder for shared guards, filters, and cross-cutting helpers. |
|
|
305
|
+
| `src/config/` | Convention | Recommended folder for environment schemas and static configurations. |
|
|
306
|
+
| `tests/` | Convention | Recommended folder for integration and end-to-end test suites. |
|
|
394
307
|
|
|
395
308
|
---
|
|
396
309
|
|
|
397
|
-
##
|
|
310
|
+
## Next Steps
|
|
398
311
|
|
|
399
|
-
-
|
|
400
|
-
-
|
|
401
|
-
-
|
|
402
|
-
-
|
|
403
|
-
- `exis generate <type>` — scaffold a route, boundary, service, or schema
|
|
404
|
-
- `exis add <package>` — install and wire an official package (e.g. `exis add database`)
|
|
312
|
+
- **[Routing & Endpoints](/docs/routing)**: Detailed routing guide covering route methods, parameter extraction, and route grouping.
|
|
313
|
+
- **[Validation Guide](/docs/validation)**: Comprehensive reference for building schemas with `tex`.
|
|
314
|
+
- **[Boundaries](/docs/boundaries)**: Scoped middleware execution, CORS cascades, and error boundary wrappers.
|
|
315
|
+
- **[Configuration](/docs/config)**: Complete options reference for `exis.config.ts`.
|