@venizia/ignis-docs 0.0.1-8 → 0.0.1
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/LICENSE.md +1 -0
- package/package.json +2 -2
- package/wiki/changelogs/2025-12-16-initial-architecture.md +145 -0
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +300 -0
- package/wiki/changelogs/2025-12-17-refactor.md +90 -0
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +130 -0
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +249 -0
- package/wiki/changelogs/index.md +33 -0
- package/wiki/changelogs/planned-transaction-support.md +216 -0
- package/wiki/changelogs/template.md +123 -0
- package/wiki/get-started/5-minute-quickstart.md +1 -1
- package/wiki/get-started/best-practices/api-usage-examples.md +12 -10
- package/wiki/get-started/best-practices/architectural-patterns.md +2 -2
- package/wiki/get-started/best-practices/common-pitfalls.md +7 -5
- package/wiki/get-started/best-practices/contribution-workflow.md +2 -0
- package/wiki/get-started/best-practices/data-modeling.md +91 -40
- package/wiki/get-started/best-practices/security-guidelines.md +3 -1
- package/wiki/get-started/building-a-crud-api.md +63 -78
- package/wiki/get-started/core-concepts/application.md +72 -3
- package/wiki/get-started/core-concepts/bootstrapping.md +566 -0
- package/wiki/get-started/core-concepts/components.md +4 -2
- package/wiki/get-started/core-concepts/controllers.md +14 -14
- package/wiki/get-started/core-concepts/persistent.md +383 -431
- package/wiki/get-started/core-concepts/services.md +21 -27
- package/wiki/get-started/quickstart.md +1 -1
- package/wiki/references/base/bootstrapping.md +789 -0
- package/wiki/references/base/components.md +1 -1
- package/wiki/references/base/controllers.md +40 -16
- package/wiki/references/base/datasources.md +195 -33
- package/wiki/references/base/dependency-injection.md +98 -5
- package/wiki/references/base/models.md +398 -28
- package/wiki/references/base/repositories.md +475 -22
- package/wiki/references/base/services.md +2 -2
- package/wiki/references/components/authentication.md +228 -10
- package/wiki/references/components/health-check.md +1 -1
- package/wiki/references/components/index.md +1 -1
- package/wiki/references/components/swagger.md +1 -1
- package/wiki/references/helpers/error.md +2 -2
- package/wiki/references/helpers/inversion.md +8 -3
- package/wiki/references/src-details/boot.md +379 -0
- package/wiki/references/src-details/core.md +8 -7
- package/wiki/references/src-details/inversion.md +4 -4
- package/wiki/references/utilities/request.md +16 -7
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# Bootstrapping
|
|
2
|
+
|
|
3
|
+
> **Core Concept**: Automatic artifact discovery and loading during application startup
|
|
4
|
+
|
|
5
|
+
## What is Bootstrapping?
|
|
6
|
+
|
|
7
|
+
Bootstrapping is the process of automatically discovering and loading application artifacts (controllers, services, repositories, datasources) during application initialization. Instead of manually registering each component, the boot system scans your project directory and automatically loads everything that matches configured patterns.
|
|
8
|
+
|
|
9
|
+
## Why Bootstrap?
|
|
10
|
+
|
|
11
|
+
### Without Boot System (Manual Registration)
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
export class Application extends BaseApplication {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(configs);
|
|
17
|
+
|
|
18
|
+
// Manual registration - tedious and error-prone
|
|
19
|
+
this.dataSource(PostgresDataSource);
|
|
20
|
+
this.dataSource(MongoDataSource);
|
|
21
|
+
|
|
22
|
+
this.repository(UserRepository);
|
|
23
|
+
this.repository(ProductRepository);
|
|
24
|
+
this.repository(OrderRepository);
|
|
25
|
+
this.repository(CustomerRepository);
|
|
26
|
+
// ... 50+ more repositories
|
|
27
|
+
|
|
28
|
+
this.service(AuthService);
|
|
29
|
+
this.service(UserService);
|
|
30
|
+
this.service(ProductService);
|
|
31
|
+
// ... 50+ more services
|
|
32
|
+
|
|
33
|
+
this.controller(AuthController);
|
|
34
|
+
this.controller(UserController);
|
|
35
|
+
this.controller(ProductController);
|
|
36
|
+
// ... 50+ more controllers
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Problems:**
|
|
42
|
+
- **Repetitive** - Every new artifact requires manual registration
|
|
43
|
+
- **Error-prone** - Easy to forget registering new artifacts
|
|
44
|
+
- **Maintenance burden** - Constructor grows as application grows
|
|
45
|
+
- **Merge conflicts** - Multiple developers editing same file
|
|
46
|
+
|
|
47
|
+
### With Boot System (Auto-discovery)
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
export const appConfigs: IApplicationConfigs = {
|
|
51
|
+
name: 'MyApp',
|
|
52
|
+
bootOptions: {
|
|
53
|
+
datasources: { dirs: ['datasources'] },
|
|
54
|
+
repositories: { dirs: ['repositories'] },
|
|
55
|
+
services: { dirs: ['services'] },
|
|
56
|
+
controllers: { dirs: ['controllers'] }
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export class Application extends BaseApplication {
|
|
61
|
+
constructor() {
|
|
62
|
+
super(appConfigs);
|
|
63
|
+
// That's it! Everything auto-discovered and registered
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Benefits:**
|
|
69
|
+
- ✅ **Convention-based** - Follow naming patterns, framework does the rest
|
|
70
|
+
- ✅ **Scalable** - Add 100 controllers without changing application code
|
|
71
|
+
- ✅ **Clean** - No constructor bloat
|
|
72
|
+
- ✅ **Team-friendly** - No merge conflicts on registration
|
|
73
|
+
|
|
74
|
+
## How It Works
|
|
75
|
+
|
|
76
|
+
### Three-Phase Boot Process
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
1. CONFIGURE → 2. DISCOVER → 3. LOAD
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Phase 1: Configure
|
|
83
|
+
|
|
84
|
+
Each booter configures its discovery patterns:
|
|
85
|
+
- Which directories to scan
|
|
86
|
+
- Which file extensions to match
|
|
87
|
+
- Whether to scan subdirectories
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// ControllerBooter configures itself
|
|
91
|
+
protected override getDefaultDirs(): string[] {
|
|
92
|
+
return ['controllers'];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected override getDefaultExtensions(): string[] {
|
|
96
|
+
return ['.controller.js'];
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Phase 2: Discover
|
|
101
|
+
|
|
102
|
+
Booters scan the filesystem for matching files:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
Project Root
|
|
106
|
+
├── controllers/
|
|
107
|
+
│ ├── auth.controller.js ✓ discovered
|
|
108
|
+
│ ├── user.controller.js ✓ discovered
|
|
109
|
+
│ └── helpers/
|
|
110
|
+
│ └── validator.js ✗ doesn't match pattern
|
|
111
|
+
├── services/
|
|
112
|
+
│ └── user.service.js ✓ discovered (by ServiceBooter)
|
|
113
|
+
└── repositories/
|
|
114
|
+
└── user.repository.js ✓ discovered (by RepositoryBooter)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Phase 3: Load
|
|
118
|
+
|
|
119
|
+
Booters load discovered classes and bind them to the container:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Pseudo-code of what happens
|
|
123
|
+
for (const file of discoveredFiles) {
|
|
124
|
+
const module = await import(file);
|
|
125
|
+
for (const exported of Object.values(module)) {
|
|
126
|
+
if (isClass(exported)) {
|
|
127
|
+
app.bind({ key: `controllers.${exported.name}` }).toClass(exported);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Boot Options
|
|
134
|
+
|
|
135
|
+
Configure discovery patterns for each artifact type.
|
|
136
|
+
|
|
137
|
+
### Basic Configuration
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const bootOptions: IBootOptions = {
|
|
141
|
+
controllers: {
|
|
142
|
+
dirs: ['controllers'], // where to look
|
|
143
|
+
extensions: ['.controller.js'], // what to match
|
|
144
|
+
isNested: true // scan subdirectories
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Multiple Directories
|
|
150
|
+
|
|
151
|
+
Scan multiple directories for the same artifact type:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const bootOptions: IBootOptions = {
|
|
155
|
+
controllers: {
|
|
156
|
+
dirs: [
|
|
157
|
+
'controllers/private', // admin controllers
|
|
158
|
+
'controllers/public' // public API controllers
|
|
159
|
+
],
|
|
160
|
+
extensions: ['.controller.js'],
|
|
161
|
+
isNested: true
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Multiple Extensions
|
|
167
|
+
|
|
168
|
+
Support both JavaScript and TypeScript:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const bootOptions: IBootOptions = {
|
|
172
|
+
services: {
|
|
173
|
+
dirs: ['services'],
|
|
174
|
+
extensions: ['.service.js', '.service.ts'],
|
|
175
|
+
isNested: true
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Custom Glob Pattern
|
|
181
|
+
|
|
182
|
+
Override default pattern with custom glob:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
const bootOptions: IBootOptions = {
|
|
186
|
+
repositories: {
|
|
187
|
+
// Custom pattern - matches any .repo.js file in data-access subdirectories
|
|
188
|
+
glob: 'data-access/**/*.repo.js'
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Disable Subdirectory Scanning
|
|
194
|
+
|
|
195
|
+
Only scan root level of directory:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
const bootOptions: IBootOptions = {
|
|
199
|
+
controllers: {
|
|
200
|
+
dirs: ['controllers'],
|
|
201
|
+
extensions: ['.controller.js'],
|
|
202
|
+
isNested: false // only scan controllers/*.controller.js, not subdirs
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Built-in Booters
|
|
208
|
+
|
|
209
|
+
The framework provides four built-in booters:
|
|
210
|
+
|
|
211
|
+
### DatasourceBooter
|
|
212
|
+
|
|
213
|
+
| Setting | Default |
|
|
214
|
+
|---------|---------|
|
|
215
|
+
| Directories | `['datasources']` |
|
|
216
|
+
| Extensions | `['.datasource.js']` |
|
|
217
|
+
| Binding Key | `datasources.{ClassName}` |
|
|
218
|
+
|
|
219
|
+
**Discovers:**
|
|
220
|
+
- `datasources/postgres.datasource.js` → `PostgresDataSource`
|
|
221
|
+
- `datasources/mongo.datasource.js` → `MongoDataSource`
|
|
222
|
+
|
|
223
|
+
### RepositoryBooter
|
|
224
|
+
|
|
225
|
+
| Setting | Default |
|
|
226
|
+
|---------|---------|
|
|
227
|
+
| Directories | `['repositories']` |
|
|
228
|
+
| Extensions | `['.repository.js']` |
|
|
229
|
+
| Binding Key | `repositories.{ClassName}` |
|
|
230
|
+
|
|
231
|
+
**Discovers:**
|
|
232
|
+
- `repositories/user.repository.js` → `UserRepository`
|
|
233
|
+
- `repositories/product/main.repository.js` → `MainRepository`
|
|
234
|
+
|
|
235
|
+
### ServiceBooter
|
|
236
|
+
|
|
237
|
+
| Setting | Default |
|
|
238
|
+
|---------|---------|
|
|
239
|
+
| Directories | `['services']` |
|
|
240
|
+
| Extensions | `['.service.js']` |
|
|
241
|
+
| Binding Key | `services.{ClassName}` |
|
|
242
|
+
|
|
243
|
+
**Discovers:**
|
|
244
|
+
- `services/auth.service.js` → `AuthService`
|
|
245
|
+
- `services/user/profile.service.js` → `ProfileService`
|
|
246
|
+
|
|
247
|
+
### ControllerBooter
|
|
248
|
+
|
|
249
|
+
| Setting | Default |
|
|
250
|
+
|---------|---------|
|
|
251
|
+
| Directories | `['controllers']` |
|
|
252
|
+
| Extensions | `['.controller.js']` |
|
|
253
|
+
| Binding Key | `controllers.{ClassName}` |
|
|
254
|
+
|
|
255
|
+
**Discovers:**
|
|
256
|
+
- `controllers/auth.controller.js` → `AuthController`
|
|
257
|
+
- `controllers/api/user.controller.js` → `UserController`
|
|
258
|
+
|
|
259
|
+
## Execution Order
|
|
260
|
+
|
|
261
|
+
Boot system respects dependency order:
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
1. DatasourceBooter → Datasources must be available first
|
|
265
|
+
2. RepositoryBooter → Repositories need datasources
|
|
266
|
+
3. ServiceBooter → Services may use repositories
|
|
267
|
+
4. ControllerBooter → Controllers use services
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
This ensures dependencies are available when artifacts are constructed.
|
|
271
|
+
|
|
272
|
+
## When Boot Runs
|
|
273
|
+
|
|
274
|
+
### Automatic Boot
|
|
275
|
+
|
|
276
|
+
Boot runs automatically during `initialize()` if `bootOptions` is configured:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const app = new Application();
|
|
280
|
+
await app.start(); // initialize() → boot() → start()
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Manual Boot
|
|
284
|
+
|
|
285
|
+
Explicitly control boot execution:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const app = new Application();
|
|
289
|
+
await app.boot({
|
|
290
|
+
phases: ['configure', 'discover', 'load'],
|
|
291
|
+
booters: ['ControllerBooter', 'ServiceBooter'] // only these booters
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Partial Boot
|
|
296
|
+
|
|
297
|
+
Run only specific phases:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
await app.boot({
|
|
301
|
+
phases: ['discover'] // only discover, don't load
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## File Naming Conventions
|
|
306
|
+
|
|
307
|
+
Follow these conventions for auto-discovery:
|
|
308
|
+
|
|
309
|
+
### Controllers
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
✓ user.controller.js
|
|
313
|
+
✓ auth.controller.js
|
|
314
|
+
✓ api/product.controller.js
|
|
315
|
+
✗ user-ctrl.js // doesn't match pattern
|
|
316
|
+
✗ controller.js // no prefix
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Services
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
✓ user.service.js
|
|
323
|
+
✓ auth.service.js
|
|
324
|
+
✓ business/order.service.js
|
|
325
|
+
✗ user-svc.js // doesn't match pattern
|
|
326
|
+
✗ service.js // no prefix
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Repositories
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
✓ user.repository.js
|
|
333
|
+
✓ product.repository.js
|
|
334
|
+
✓ data/customer.repository.js
|
|
335
|
+
✗ user-repo.js // doesn't match pattern
|
|
336
|
+
✗ repository.js // no prefix
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Datasources
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
✓ postgres.datasource.js
|
|
343
|
+
✓ mongo.datasource.js
|
|
344
|
+
✓ connections/redis.datasource.js
|
|
345
|
+
✗ postgres-ds.js // doesn't match pattern
|
|
346
|
+
✗ datasource.js // no prefix
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Project Structure Examples
|
|
350
|
+
|
|
351
|
+
### Simple Structure
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
src/
|
|
355
|
+
├── datasources/
|
|
356
|
+
│ └── postgres.datasource.js
|
|
357
|
+
├── repositories/
|
|
358
|
+
│ ├── user.repository.js
|
|
359
|
+
│ └── product.repository.js
|
|
360
|
+
├── services/
|
|
361
|
+
│ ├── auth.service.js
|
|
362
|
+
│ └── user.service.js
|
|
363
|
+
└── controllers/
|
|
364
|
+
├── auth.controller.js
|
|
365
|
+
└── user.controller.js
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Boot Config:**
|
|
369
|
+
```typescript
|
|
370
|
+
bootOptions: {
|
|
371
|
+
datasources: { dirs: ['datasources'] },
|
|
372
|
+
repositories: { dirs: ['repositories'] },
|
|
373
|
+
services: { dirs: ['services'] },
|
|
374
|
+
controllers: { dirs: ['controllers'] }
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Feature-based Structure
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
src/
|
|
382
|
+
├── features/
|
|
383
|
+
│ ├── auth/
|
|
384
|
+
│ │ ├── auth.controller.js
|
|
385
|
+
│ │ ├── auth.service.js
|
|
386
|
+
│ │ └── auth.repository.js
|
|
387
|
+
│ └── user/
|
|
388
|
+
│ ├── user.controller.js
|
|
389
|
+
│ ├── user.service.js
|
|
390
|
+
│ └── user.repository.js
|
|
391
|
+
└── datasources/
|
|
392
|
+
└── postgres.datasource.js
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Boot Config:**
|
|
396
|
+
```typescript
|
|
397
|
+
bootOptions: {
|
|
398
|
+
datasources: { dirs: ['datasources'] },
|
|
399
|
+
repositories: { glob: 'features/**/*.repository.js' },
|
|
400
|
+
services: { glob: 'features/**/*.service.js' },
|
|
401
|
+
controllers: { glob: 'features/**/*.controller.js' }
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Layered Structure
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
src/
|
|
409
|
+
├── data/
|
|
410
|
+
│ ├── datasources/
|
|
411
|
+
│ │ └── postgres.datasource.js
|
|
412
|
+
│ └── repositories/
|
|
413
|
+
│ └── user.repository.js
|
|
414
|
+
├── business/
|
|
415
|
+
│ └── services/
|
|
416
|
+
│ └── user.service.js
|
|
417
|
+
└── api/
|
|
418
|
+
└── controllers/
|
|
419
|
+
└── user.controller.js
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**Boot Config:**
|
|
423
|
+
```typescript
|
|
424
|
+
bootOptions: {
|
|
425
|
+
datasources: { dirs: ['data/datasources'] },
|
|
426
|
+
repositories: { dirs: ['data/repositories'] },
|
|
427
|
+
services: { dirs: ['business/services'] },
|
|
428
|
+
controllers: { dirs: ['api/controllers'] }
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Custom Booters
|
|
433
|
+
|
|
434
|
+
Create custom booters for new artifact types:
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { BaseArtifactBooter, IBooterOptions } from '@venizia/ignis-boot';
|
|
438
|
+
import { inject } from '@venizia/ignis-inversion';
|
|
439
|
+
|
|
440
|
+
export class MiddlewareBooter extends BaseArtifactBooter {
|
|
441
|
+
constructor(
|
|
442
|
+
@inject({ key: '@app/project_root' }) root: string,
|
|
443
|
+
@inject({ key: '@app/instance' }) private app: IApplication,
|
|
444
|
+
@inject({ key: '@app/boot-options' }) bootOptions: IBootOptions,
|
|
445
|
+
) {
|
|
446
|
+
super({
|
|
447
|
+
scope: MiddlewareBooter.name,
|
|
448
|
+
root,
|
|
449
|
+
artifactOptions: bootOptions.middlewares ?? {}
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
protected getDefaultDirs(): string[] {
|
|
454
|
+
return ['middlewares'];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
protected getDefaultExtensions(): string[] {
|
|
458
|
+
return ['.middleware.js'];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
protected async bind(): Promise<void> {
|
|
462
|
+
for (const cls of this.loadedClasses) {
|
|
463
|
+
this.app.bind({ key: `middlewares.${cls.name}` }).toClass(cls);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**Register Custom Booter:**
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
export class Application extends BaseApplication {
|
|
473
|
+
override async initialize() {
|
|
474
|
+
// Register custom booter
|
|
475
|
+
this.booter(MiddlewareBooter);
|
|
476
|
+
|
|
477
|
+
await super.initialize();
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## Performance Considerations
|
|
483
|
+
|
|
484
|
+
### Boot Time
|
|
485
|
+
|
|
486
|
+
Boot adds minimal overhead:
|
|
487
|
+
- **Configure phase**: < 1ms per booter
|
|
488
|
+
- **Discover phase**: 10-50ms (depends on filesystem)
|
|
489
|
+
- **Load phase**: 50-200ms (depends on artifact count)
|
|
490
|
+
|
|
491
|
+
**Total**: Typically 100-300ms for medium-sized applications.
|
|
492
|
+
|
|
493
|
+
### Development vs Production
|
|
494
|
+
|
|
495
|
+
Boot is most valuable in **production** where artifact count is high. In **development**, the overhead is negligible.
|
|
496
|
+
|
|
497
|
+
### Optimization Tips
|
|
498
|
+
|
|
499
|
+
1. **Limit nested scanning** - Set `isNested: false` when possible
|
|
500
|
+
2. **Specific patterns** - Use precise glob patterns
|
|
501
|
+
3. **Skip unused booters** - Only enable needed booters
|
|
502
|
+
4. **Pre-compiled bundles** - For serverless, consider bundling
|
|
503
|
+
|
|
504
|
+
## Troubleshooting
|
|
505
|
+
|
|
506
|
+
### Artifacts Not Discovered
|
|
507
|
+
|
|
508
|
+
**Problem:** Created `user.controller.js` but not loaded.
|
|
509
|
+
|
|
510
|
+
**Solutions:**
|
|
511
|
+
1. Check file naming: Must match pattern (e.g., `*.controller.js`)
|
|
512
|
+
2. Check directory: File must be in configured dirs
|
|
513
|
+
3. Check extension: Must match configured extensions
|
|
514
|
+
4. Enable debug logging: See what's discovered
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
// Enable debug logs
|
|
518
|
+
process.env.LOG_LEVEL = 'debug';
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Wrong Binding Order
|
|
522
|
+
|
|
523
|
+
**Problem:** Repository tries to use datasource before it's available.
|
|
524
|
+
|
|
525
|
+
**Solution:** Boot system handles this automatically. Datasources are always loaded before repositories. If you have custom booters, register them in correct order:
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
this.booter(CustomDatasourceBooter);
|
|
529
|
+
this.booter(CustomRepositoryBooter); // after datasource
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Custom Pattern Not Working
|
|
533
|
+
|
|
534
|
+
**Problem:** Custom glob pattern doesn't match files.
|
|
535
|
+
|
|
536
|
+
**Solution:** Test pattern with glob tool:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
# From project root
|
|
540
|
+
npx glob "your-pattern/**/*.controller.js"
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Best Practices
|
|
544
|
+
|
|
545
|
+
### ✅ DO
|
|
546
|
+
|
|
547
|
+
- Follow naming conventions consistently
|
|
548
|
+
- Use boot system for applications with > 5 artifacts per type
|
|
549
|
+
- Organize files by feature or layer
|
|
550
|
+
- Keep boot options in config file
|
|
551
|
+
- Use debug logging during development
|
|
552
|
+
|
|
553
|
+
### ❌ DON'T
|
|
554
|
+
|
|
555
|
+
- Mix manual and auto registration (choose one approach)
|
|
556
|
+
- Use boot for tiny applications (< 5 total artifacts)
|
|
557
|
+
- Override default patterns without good reason
|
|
558
|
+
- Skip subdirectories if you have nested structure
|
|
559
|
+
- Ignore boot errors (they indicate misconfiguration)
|
|
560
|
+
|
|
561
|
+
## Related Documentation
|
|
562
|
+
|
|
563
|
+
- [Boot Package Reference](/references/src-details/boot.md)
|
|
564
|
+
- [Application Concepts](/get-started/core-concepts/application.md)
|
|
565
|
+
- [Dependency Injection](/references/base/dependency-injection.md)
|
|
566
|
+
- [Building a CRUD API](/get-started/building-a-crud-api.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Components
|
|
2
2
|
|
|
3
|
-
Components are reusable, pluggable modules that encapsulate features
|
|
3
|
+
Components are reusable, pluggable modules that encapsulate a group of related features. A component acts as a powerful container for various resources—including providers, services, controllers, repositories, and even entire mini-applications—making it easy to share and integrate complex functionality across projects.
|
|
4
4
|
|
|
5
5
|
> **Deep Dive:** See [Components Reference](../../references/base/components.md) for technical details.
|
|
6
6
|
|
|
@@ -8,9 +8,11 @@ Components are reusable, pluggable modules that encapsulate features like authen
|
|
|
8
8
|
|
|
9
9
|
A component is a class that extends `BaseComponent` and is responsible for:
|
|
10
10
|
|
|
11
|
-
- **Binding Dependencies**: Registering services, controllers, providers, or other resources with the application's dependency injection container.
|
|
11
|
+
- **Binding Dependencies**: Registering services, controllers, repositories, providers, or other resources with the application's dependency injection container.
|
|
12
12
|
- **Configuring Features**: Setting up middlewares, initializing services, or performing any other setup required for the feature to work.
|
|
13
13
|
|
|
14
|
+
A single component can bundle everything needed for a specific domain—for example, an "AuthComponent" might include multiple services for token management, repositories for user data, and controllers for login/signup endpoints, essentially functioning as a plug-and-play mini-application.
|
|
15
|
+
|
|
14
16
|
`Ignis` comes with several built-in components, which you can explore in the [**Components Reference**](../../references/components/) section:
|
|
15
17
|
|
|
16
18
|
- **`AuthenticateComponent`**: Sets up JWT-based authentication.
|
|
@@ -9,7 +9,7 @@ Controllers handle HTTP requests and return responses - they're your API endpoin
|
|
|
9
9
|
Extend `BaseController` and use decorators to define routes:
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
|
-
import { BaseController, controller, get, jsonResponse, z } from '@venizia/ignis';
|
|
12
|
+
import { BaseController, controller, get, jsonResponse, z, HTTP } from '@venizia/ignis';
|
|
13
13
|
import { Context } from 'hono';
|
|
14
14
|
|
|
15
15
|
@controller({ path: '/users' })
|
|
@@ -29,7 +29,7 @@ export class UserController extends BaseController {
|
|
|
29
29
|
},
|
|
30
30
|
})
|
|
31
31
|
getAllUsers(c: Context) {
|
|
32
|
-
return c.json([{ id: '1', name: 'John Doe' }]);
|
|
32
|
+
return c.json([{ id: '1', name: 'John Doe' }], HTTP.ResultCodes.RS_2.Ok);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
```
|
|
@@ -177,7 +177,7 @@ const GetUsersRoute = {
|
|
|
177
177
|
this.defineRoute({
|
|
178
178
|
configs: GetUsersRoute,
|
|
179
179
|
handler: (c: TRouteContext<typeof GetUsersRoute>) => { // Return type is automatically inferred
|
|
180
|
-
return c.json([{ id: 1, name: 'John Doe' }]);
|
|
180
|
+
return c.json([{ id: 1, name: 'John Doe' }], HTTP.ResultCodes.RS_2.Ok);
|
|
181
181
|
},
|
|
182
182
|
});
|
|
183
183
|
```
|
|
@@ -187,7 +187,7 @@ this.defineRoute({
|
|
|
187
187
|
This method offers a fluent API for defining routes, similar to `defineRoute`, but structured for chaining. It also benefits from `TRouteContext` for type safety.
|
|
188
188
|
|
|
189
189
|
```typescript
|
|
190
|
-
import { jsonResponse, z, TRouteContext } from '@venizia/ignis';
|
|
190
|
+
import { jsonResponse, z, TRouteContext, HTTP } from '@venizia/ignis';
|
|
191
191
|
|
|
192
192
|
// ... inside the binding() method
|
|
193
193
|
|
|
@@ -205,7 +205,7 @@ this.bindRoute({
|
|
|
205
205
|
}).to({
|
|
206
206
|
handler: (c: TRouteContext<typeof GetUserByIdRoute>) => { // Return type is automatically inferred
|
|
207
207
|
const { id } = c.req.param();
|
|
208
|
-
return c.json({ id: id, name: 'John Doe' });
|
|
208
|
+
return c.json({ id: id, name: 'John Doe' }, HTTP.ResultCodes.RS_2.Ok);
|
|
209
209
|
},
|
|
210
210
|
});
|
|
211
211
|
```
|
|
@@ -255,17 +255,17 @@ export class ConfigurationController extends _Controller {
|
|
|
255
255
|
```
|
|
256
256
|
The `ControllerFactory.defineCrudController` method automatically sets up the following routes based on your entity schema:
|
|
257
257
|
|
|
258
|
-
| Name | Method | Path | Description |
|
|
258
|
+
| Route Name | Method | Path | Description |
|
|
259
259
|
| :--- | :--- | :--- | :--- |
|
|
260
260
|
| `count` | `GET` | `/count` | Get the number of records matching a filter. |
|
|
261
261
|
| `find` | `GET` | `/` | Retrieve all records matching a filter. |
|
|
262
262
|
| `findById` | `GET` | `/:id` | Retrieve a single record by its ID. |
|
|
263
263
|
| `findOne` | `GET` | `/find-one` | Retrieve a single record matching a filter. |
|
|
264
264
|
| `create` | `POST` | `/` | Create a new record. |
|
|
265
|
-
| `updateById` | `PATCH` | `/:id` | Update a record by its ID. |
|
|
266
|
-
| `
|
|
267
|
-
| `deleteById` | `DELETE` | `/:id` | Delete a record by its ID. |
|
|
268
|
-
| `
|
|
265
|
+
| `updateById` | `PATCH` | `/:id` | Update a single record by its ID. |
|
|
266
|
+
| `updateBy` | `PATCH` | `/` | Update multiple records matching a `where` filter. |
|
|
267
|
+
| `deleteById` | `DELETE` | `/:id` | Delete a single record by its ID. |
|
|
268
|
+
| `deleteBy` | `DELETE` | `/` | Delete multiple records matching a `where` filter. |
|
|
269
269
|
|
|
270
270
|
:::info Customization
|
|
271
271
|
The `ControllerFactory` is highly customizable. You can override the Zod schemas for any of the generated routes to add, remove, or modify fields for request validation and response shapes. You can also configure other behaviors, like making delete operations return the deleted records.
|
|
@@ -368,7 +368,7 @@ When you define Zod schemas in your route's `request` configuration (whether wit
|
|
|
368
368
|
|
|
369
369
|
```typescript
|
|
370
370
|
import { z } from '@hono/zod-openapi';
|
|
371
|
-
import { jsonContent, put } from '@venizia/ignis';
|
|
371
|
+
import { jsonContent, put, HTTP } from '@venizia/ignis';
|
|
372
372
|
|
|
373
373
|
// ... inside a controller class
|
|
374
374
|
|
|
@@ -397,7 +397,7 @@ updateUser(c: Context) {
|
|
|
397
397
|
console.log('Notification is enabled.');
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
return c.json({ success: true, id, ...userUpdateData });
|
|
400
|
+
return c.json({ success: true, id, ...userUpdateData }, HTTP.ResultCodes.RS_2.Ok);
|
|
401
401
|
}
|
|
402
402
|
```
|
|
403
403
|
|
|
@@ -405,7 +405,7 @@ Using `c.req.valid()` is the recommended way to access request data as it ensure
|
|
|
405
405
|
|
|
406
406
|
```typescript
|
|
407
407
|
import { z } from '@hono/zod-openapi';
|
|
408
|
-
import { jsonContent, put, TRouteContext } from '@venizia/ignis';
|
|
408
|
+
import { jsonContent, put, TRouteContext, HTTP } from '@venizia/ignis';
|
|
409
409
|
|
|
410
410
|
// ... inside a controller class
|
|
411
411
|
|
|
@@ -434,7 +434,7 @@ updateUser(c: TRouteContext<typeof updateUserConfig>) {
|
|
|
434
434
|
console.log('Notification is enabled.');
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
-
return c.json({ success: true, id, ...userUpdateData });
|
|
437
|
+
return c.json({ success: true, id, ...userUpdateData }, HTTP.ResultCodes.RS_2.Ok);
|
|
438
438
|
}
|
|
439
439
|
```
|
|
440
440
|
|