@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Deep Dive: Components
|
|
2
2
|
|
|
3
|
-
Technical reference for `BaseComponent
|
|
3
|
+
Technical reference for `BaseComponent`—the foundation for creating reusable, pluggable features in Ignis. Components are powerful containers that can group together multiple providers, services, controllers, repositories, and even entire mini-applications into a single, redistributable module.
|
|
4
4
|
|
|
5
5
|
**File:** `packages/core/src/base/components/base.ts`
|
|
6
6
|
|
|
@@ -65,7 +65,7 @@ export class MyFeatureController extends BaseController {
|
|
|
65
65
|
|
|
66
66
|
@api({ configs: MyRouteConfig })
|
|
67
67
|
getData(c: TRouteContext<typeof MyRouteConfig>) { // Return type is automatically inferred and validated
|
|
68
|
-
return c.json({ success: true });
|
|
68
|
+
return c.json({ success: true }, HTTP.ResultCodes.RS_2.Ok);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
```
|
|
@@ -83,7 +83,7 @@ For convenience, `Ignis` provides decorator shortcuts for each HTTP method: Thes
|
|
|
83
83
|
**Example using `@get` and `@post` with type inference:**
|
|
84
84
|
|
|
85
85
|
```typescript
|
|
86
|
-
import { get, post, z, jsonContent, jsonResponse, Authentication, TRouteContext } from '@venizia/ignis';
|
|
86
|
+
import { get, post, z, jsonContent, jsonResponse, Authentication, TRouteContext, HTTP } from '@venizia/ignis';
|
|
87
87
|
|
|
88
88
|
// Define route configs as const for full type inference
|
|
89
89
|
const USER_ROUTES = {
|
|
@@ -125,20 +125,20 @@ const USER_ROUTES = {
|
|
|
125
125
|
|
|
126
126
|
@get({ configs: USER_ROUTES.listUsers })
|
|
127
127
|
getAllUsers(c: TRouteContext<typeof USER_ROUTES.listUsers>) { // Return type is automatically inferred
|
|
128
|
-
return c.json([{ id: '1', name: 'John Doe' }]);
|
|
128
|
+
return c.json([{ id: '1', name: 'John Doe' }], HTTP.ResultCodes.RS_2.Ok);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
@get({ configs: USER_ROUTES.getUser })
|
|
132
132
|
getUserById(c: TRouteContext<typeof USER_ROUTES.getUser>) { // Return type is automatically inferred
|
|
133
133
|
const { id } = c.req.valid('param'); // id is typed as string
|
|
134
|
-
return c.json({ id, name: 'John Doe' });
|
|
134
|
+
return c.json({ id, name: 'John Doe' }, HTTP.ResultCodes.RS_2.Ok);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
@post({ configs: USER_ROUTES.createUser })
|
|
138
138
|
createUser(c: TRouteContext<typeof USER_ROUTES.createUser>) { // Return type is automatically inferred
|
|
139
139
|
const { name } = c.req.valid('json'); // name is typed as string
|
|
140
140
|
const newUser = { id: '2', name };
|
|
141
|
-
return c.json(newUser,
|
|
141
|
+
return c.json(newUser, HTTP.ResultCodes.RS_2.Created); // Return type is validated
|
|
142
142
|
}
|
|
143
143
|
```
|
|
144
144
|
|
|
@@ -171,17 +171,21 @@ export class HealthCheckController extends BaseController {
|
|
|
171
171
|
@api({ configs: HEALTH_CHECK_ROUTES['/ping'] })
|
|
172
172
|
ping(c: TRouteContext<typeof HEALTH_CHECK_ROUTES['/ping']>) { // Return type is automatically inferred
|
|
173
173
|
const { message } = c.req.valid('json');
|
|
174
|
-
return c.json({ pong: message });
|
|
174
|
+
return c.json({ pong: message }, HTTP.ResultCodes.RS_2.Ok);
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
### Manual Route Definition Methods
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
For advanced use cases or when you prefer a non-decorator approach, you can define routes manually using `defineRoute` and `bindRoute` methods inside the `binding()` method.
|
|
182
182
|
|
|
183
|
-
:::tip
|
|
184
|
-
|
|
183
|
+
:::tip When to Use Manual Definition
|
|
184
|
+
Manual route definition is useful for:
|
|
185
|
+
- Dynamically generating routes based on configuration
|
|
186
|
+
- Conditional route registration (feature flags)
|
|
187
|
+
- Developers who prefer non-decorator syntax (coming from Express/Fastify)
|
|
188
|
+
- Complex routing logic that benefits from programmatic control
|
|
185
189
|
:::
|
|
186
190
|
|
|
187
191
|
#### `defineRoute`
|
|
@@ -291,17 +295,17 @@ This factory method returns a `BaseController` class that is already set up with
|
|
|
291
295
|
|
|
292
296
|
**Note:** The returned class is dynamically named using `controller.name` from the options. This ensures that when registered with `app.controller()`, the class has a proper name for binding keys and debugging (e.g., `ConfigurationController` instead of an anonymous class).
|
|
293
297
|
|
|
294
|
-
| Name | Method | Path | Description |
|
|
298
|
+
| Route Name | Method | Path | Description |
|
|
295
299
|
| :--- | :--- | :--- | :--- |
|
|
296
300
|
| `count` | `GET` | `/count` | Get the number of records matching a filter. |
|
|
297
301
|
| `find` | `GET` | `/` | Retrieve all records matching a filter. |
|
|
298
302
|
| `findById` | `GET` | `/:id` | Retrieve a single record by its ID. |
|
|
299
303
|
| `findOne` | `GET` | `/find-one` | Retrieve a single record matching a filter. |
|
|
300
304
|
| `create` | `POST` | `/` | Create a new record. |
|
|
301
|
-
| `updateById` | `PATCH` | `/:id` | Update a record by its ID. |
|
|
302
|
-
| `
|
|
303
|
-
| `deleteById` | `DELETE` | `/:id` | Delete a record by its ID. |
|
|
304
|
-
| `
|
|
305
|
+
| `updateById` | `PATCH` | `/:id` | Update a single record by its ID. |
|
|
306
|
+
| `updateBy` | `PATCH` | `/` | Update multiple records matching a `where` filter. |
|
|
307
|
+
| `deleteById` | `DELETE` | `/:id` | Delete a single record by its ID. |
|
|
308
|
+
| `deleteBy` | `DELETE` | `/` | Delete multiple records matching a `where` filter. |
|
|
305
309
|
|
|
306
310
|
### `ICrudControllerOptions<EntitySchema>`
|
|
307
311
|
|
|
@@ -311,10 +315,30 @@ This factory method returns a `BaseController` class that is already set up with
|
|
|
311
315
|
| `repository.name` | `string` | The binding key name of the repository associated with this entity (e.g., `'ConfigurationRepository'`). |
|
|
312
316
|
| `controller.name` | `string` | A unique name for the generated controller (e.g., `'ConfigurationController'`). |
|
|
313
317
|
| `controller.basePath`| `string` | The base path for all routes in this CRUD controller (e.g., `'/configurations'`). |
|
|
318
|
+
| `controller.readonly` | `boolean` | If `true`, only read operations (find, findOne, findById, count) are generated. Write operations are excluded. Defaults to `false`. |
|
|
314
319
|
| `controller.isStrict` | `boolean` | If `true`, query parameters like `where` will be strictly validated. Defaults to `true`. |
|
|
315
320
|
| `controller.defaultLimit`| `number` | The default limit for `find` operations. Defaults to `10`. |
|
|
316
|
-
| `schema` | `object` | An optional object to override the default Zod schemas for specific CRUD endpoints
|
|
317
|
-
| `doDeleteWithReturn` | `boolean` | If `true`, the `deleteById` and `
|
|
321
|
+
| `schema` | `object` | An optional object to override the default Zod schemas for specific CRUD endpoints. See schema options below. |
|
|
322
|
+
| `doDeleteWithReturn` | `boolean` | If `true`, the `deleteById` and `deleteBy` endpoints will return the deleted record(s) in the response body. Defaults to `false`. |
|
|
323
|
+
|
|
324
|
+
### Schema Override Options
|
|
325
|
+
|
|
326
|
+
The `schema` option allows fine-grained control over request/response validation and OpenAPI documentation:
|
|
327
|
+
|
|
328
|
+
| Schema Option | Description |
|
|
329
|
+
| :--- | :--- |
|
|
330
|
+
| `count` | Override response schema for count endpoint |
|
|
331
|
+
| `find` | Override response schema for find endpoint |
|
|
332
|
+
| `findOne` | Override response schema for findOne endpoint |
|
|
333
|
+
| `findById` | Override response schema for findById endpoint |
|
|
334
|
+
| `create` | Override response schema for create endpoint |
|
|
335
|
+
| `createRequestBody` | Override request body schema for create endpoint |
|
|
336
|
+
| `updateById` | Override response schema for updateById endpoint |
|
|
337
|
+
| `updateByIdRequestBody` | Override request body schema for updateById endpoint |
|
|
338
|
+
| `updateBy` | Override response schema for updateBy (bulk update) endpoint |
|
|
339
|
+
| `updateByRequestBody` | Override request body schema for updateBy endpoint |
|
|
340
|
+
| `deleteById` | Override response schema for deleteById endpoint |
|
|
341
|
+
| `deleteBy` | Override response schema for deleteBy (bulk delete) endpoint |
|
|
318
342
|
|
|
319
343
|
### Example
|
|
320
344
|
|
|
@@ -8,9 +8,9 @@ Technical reference for DataSource classes - managing database connections in Ig
|
|
|
8
8
|
|
|
9
9
|
| Class/Interface | Purpose | Key Members |
|
|
10
10
|
|-----------------|---------|-------------|
|
|
11
|
-
| **IDataSource** | Contract for all datasources | `name`, `settings`, `connector`, `
|
|
11
|
+
| **IDataSource** | Contract for all datasources | `name`, `settings`, `connector`, `getSchema()`, `configure()` |
|
|
12
12
|
| **AbstractDataSource** | Base implementation with logging | Extends `BaseHelper` |
|
|
13
|
-
| **BaseDataSource** | Concrete class to extend |
|
|
13
|
+
| **BaseDataSource** | Concrete class to extend | Auto-discovery, driver from decorator |
|
|
14
14
|
|
|
15
15
|
## `IDataSource` Interface
|
|
16
16
|
|
|
@@ -25,7 +25,7 @@ Contract for all datasource classes in the framework.
|
|
|
25
25
|
| `name` | `string` | Datasource name |
|
|
26
26
|
| `settings` | `object` | Configuration object |
|
|
27
27
|
| `connector` | `TDatabaseConnector` | Database connector instance (e.g., Drizzle) |
|
|
28
|
-
| `
|
|
28
|
+
| `getSchema()` | Method | Returns combined Drizzle schema (auto-discovered or manual) |
|
|
29
29
|
| `configure()` | Method | Initializes the `connector` |
|
|
30
30
|
| `getConnectionString()` | Method | Returns connection string |
|
|
31
31
|
|
|
@@ -39,29 +39,60 @@ This is the top-level abstract class that implements the `IDataSource` interface
|
|
|
39
39
|
|
|
40
40
|
### `BaseDataSource`
|
|
41
41
|
|
|
42
|
-
This class extends `AbstractDataSource` and provides a constructor
|
|
42
|
+
This class extends `AbstractDataSource` and provides a constructor with **auto-discovery** support. When you create your own datasource, you extend `BaseDataSource`.
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
#### Key Features
|
|
45
|
+
|
|
46
|
+
| Feature | Description |
|
|
47
|
+
|---------|-------------|
|
|
48
|
+
| **Driver Auto-Read** | Driver is read from `@datasource` decorator - no need to pass in constructor |
|
|
49
|
+
| **Schema Auto-Discovery** | Schema is automatically built from registered `@repository` decorators |
|
|
50
|
+
| **Manual Override** | You can still manually provide schema in constructor for full control |
|
|
51
|
+
|
|
52
|
+
### Constructor Options
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
constructor(opts: {
|
|
56
|
+
name: string; // DataSource name (usually class name)
|
|
57
|
+
config: Settings; // Database connection settings
|
|
58
|
+
driver?: TDataSourceDriver; // Optional - read from @datasource if not provided
|
|
59
|
+
schema?: Schema; // Optional - auto-discovered if not provided
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Schema Auto-Discovery
|
|
64
|
+
|
|
65
|
+
When you use `@repository({ model: YourModel, dataSource: YourDataSource })`, the framework automatically:
|
|
66
|
+
|
|
67
|
+
1. Registers the model-datasource binding in the MetadataRegistry
|
|
68
|
+
2. When `getSchema()` is called, discovers all models bound to this datasource
|
|
69
|
+
3. Builds the combined schema (tables + relations) automatically
|
|
70
|
+
|
|
71
|
+
**This means you no longer need to manually merge tables and relations in the DataSource constructor!**
|
|
72
|
+
|
|
73
|
+
### Configuration Flow
|
|
45
74
|
|
|
46
75
|
1. **Your DataSource's `constructor` is called**:
|
|
47
|
-
- You call `super()` with
|
|
48
|
-
-
|
|
76
|
+
- You call `super()` with `name` and `config`
|
|
77
|
+
- Driver is automatically read from `@datasource` decorator
|
|
78
|
+
- Schema is auto-discovered from `@repository` bindings (or manually provided)
|
|
49
79
|
|
|
50
80
|
2. **`Application.registerDataSources()` is called during startup**:
|
|
51
|
-
- The application gets your `DataSource` instance from the DI container
|
|
52
|
-
- It calls the `configure()` method on your instance
|
|
81
|
+
- The application gets your `DataSource` instance from the DI container
|
|
82
|
+
- It calls the `configure()` method on your instance
|
|
53
83
|
|
|
54
84
|
3. **Your `configure()` method runs**:
|
|
55
|
-
- This is where you instantiate the Drizzle ORM
|
|
56
|
-
-
|
|
85
|
+
- This is where you instantiate the Drizzle ORM
|
|
86
|
+
- Use `this.getSchema()` to get the auto-discovered schema and pass to Drizzle
|
|
87
|
+
|
|
88
|
+
### Example Implementations
|
|
57
89
|
|
|
58
|
-
|
|
90
|
+
#### Pattern 1: Auto-Discovery (Recommended)
|
|
91
|
+
|
|
92
|
+
Simplest approach - schema is auto-discovered from repositories:
|
|
59
93
|
|
|
60
94
|
```typescript
|
|
61
95
|
// src/datasources/postgres.datasource.ts
|
|
62
|
-
import {
|
|
63
|
-
// ... import your models and relations
|
|
64
|
-
} from '@/models/entities';
|
|
65
96
|
import {
|
|
66
97
|
BaseDataSource,
|
|
67
98
|
datasource,
|
|
@@ -71,35 +102,166 @@ import {
|
|
|
71
102
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
72
103
|
import { Pool } from 'pg';
|
|
73
104
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
105
|
+
interface IDSConfigs {
|
|
106
|
+
host: string;
|
|
107
|
+
port: number;
|
|
108
|
+
database: string;
|
|
109
|
+
user: string;
|
|
110
|
+
password: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* PostgresDataSource with auto-discovery support.
|
|
115
|
+
*
|
|
116
|
+
* How it works:
|
|
117
|
+
* 1. @repository decorator binds model to datasource
|
|
118
|
+
* 2. When configure() is called, getSchema() auto-discovers all bound models
|
|
119
|
+
* 3. Drizzle is initialized with the auto-discovered schema
|
|
120
|
+
*/
|
|
121
|
+
@datasource({ driver: 'node-postgres' })
|
|
122
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
80
123
|
constructor() {
|
|
81
124
|
super({
|
|
82
125
|
name: PostgresDataSource.name,
|
|
83
|
-
|
|
84
|
-
config: {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
126
|
+
// Driver is read from @datasource decorator - no need to pass here!
|
|
127
|
+
config: {
|
|
128
|
+
host: process.env.APP_ENV_POSTGRES_HOST ?? 'localhost',
|
|
129
|
+
port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
|
|
130
|
+
database: process.env.APP_ENV_POSTGRES_DATABASE ?? 'mydb',
|
|
131
|
+
user: process.env.APP_ENV_POSTGRES_USERNAME ?? 'postgres',
|
|
132
|
+
password: process.env.APP_ENV_POSTGRES_PASSWORD ?? '',
|
|
90
133
|
},
|
|
134
|
+
// NO schema property - auto-discovered from @repository bindings!
|
|
91
135
|
});
|
|
92
136
|
}
|
|
93
137
|
|
|
94
|
-
// This method is called by the application at startup
|
|
95
138
|
override configure(): ValueOrPromise<void> {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
139
|
+
// getSchema() auto-discovers models from @repository bindings
|
|
140
|
+
const schema = this.getSchema();
|
|
141
|
+
|
|
142
|
+
// Log discovered schema for debugging
|
|
143
|
+
const schemaKeys = Object.keys(schema);
|
|
144
|
+
this.logger.debug(
|
|
145
|
+
'[configure] Auto-discovered schema | Schema + Relations (%s): %o',
|
|
146
|
+
schemaKeys.length,
|
|
147
|
+
schemaKeys,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const client = new Pool(this.settings);
|
|
151
|
+
this.connector = drizzle({ client, schema });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
With this pattern, when you define repositories:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
160
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
161
|
+
|
|
162
|
+
@repository({ model: Configuration, dataSource: PostgresDataSource })
|
|
163
|
+
export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The `PostgresDataSource.schema` will automatically include User and Configuration tables and their relations.
|
|
167
|
+
|
|
168
|
+
#### Pattern 2: Manual Schema (Full Control)
|
|
169
|
+
|
|
170
|
+
When you need explicit control over schema (e.g., subset of models, custom ordering):
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import {
|
|
174
|
+
User, userTable, userRelations,
|
|
175
|
+
Configuration, configurationTable, configurationRelations,
|
|
176
|
+
} from '@/models/entities';
|
|
177
|
+
|
|
178
|
+
@datasource({ driver: 'node-postgres' })
|
|
179
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
180
|
+
constructor() {
|
|
181
|
+
super({
|
|
182
|
+
name: PostgresDataSource.name,
|
|
183
|
+
config: {
|
|
184
|
+
host: process.env.APP_ENV_POSTGRES_HOST ?? 'localhost',
|
|
185
|
+
port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
|
|
186
|
+
database: process.env.APP_ENV_POSTGRES_DATABASE ?? 'mydb',
|
|
187
|
+
user: process.env.APP_ENV_POSTGRES_USERNAME ?? 'postgres',
|
|
188
|
+
password: process.env.APP_ENV_POSTGRES_PASSWORD ?? '',
|
|
189
|
+
},
|
|
190
|
+
// Manually provide schema using spread syntax
|
|
191
|
+
schema: {
|
|
192
|
+
[User.TABLE_NAME]: userTable,
|
|
193
|
+
[Configuration.TABLE_NAME]: configurationTable,
|
|
194
|
+
...userRelations.relations,
|
|
195
|
+
...configurationRelations.relations,
|
|
196
|
+
},
|
|
99
197
|
});
|
|
100
198
|
}
|
|
101
|
-
|
|
199
|
+
|
|
200
|
+
override configure(): ValueOrPromise<void> {
|
|
201
|
+
// When schema is manually provided, getSchema() returns it directly
|
|
202
|
+
const client = new Pool(this.settings);
|
|
203
|
+
this.connector = drizzle({ client, schema: this.getSchema() });
|
|
204
|
+
}
|
|
102
205
|
}
|
|
103
206
|
```
|
|
104
207
|
|
|
208
|
+
### @datasource Decorator
|
|
209
|
+
|
|
210
|
+
The `@datasource` decorator registers datasource metadata:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
@datasource({
|
|
214
|
+
driver: 'node-postgres', // Required - database driver
|
|
215
|
+
autoDiscovery?: true // Optional - defaults to true
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
| Option | Type | Default | Description |
|
|
220
|
+
|--------|------|---------|-------------|
|
|
221
|
+
| `driver` | `TDataSourceDriver` | - | Database driver name |
|
|
222
|
+
| `autoDiscovery` | `boolean` | `true` | Enable/disable schema auto-discovery |
|
|
223
|
+
|
|
224
|
+
### Abstract Methods
|
|
225
|
+
|
|
226
|
+
These methods must be implemented in your datasource class:
|
|
227
|
+
|
|
228
|
+
| Method | Return Type | Description |
|
|
229
|
+
|--------|-------------|-------------|
|
|
230
|
+
| `configure(opts?)` | `ValueOrPromise<void>` | Initialize the Drizzle ORM connector. Called during application startup. |
|
|
231
|
+
| `getConnectionString()` | `ValueOrPromise<string>` | Return the database connection string. |
|
|
232
|
+
|
|
233
|
+
### Optional Override Methods
|
|
234
|
+
|
|
235
|
+
These methods can be optionally overridden for connection lifecycle management:
|
|
236
|
+
|
|
237
|
+
| Method | Return Type | Description |
|
|
238
|
+
|--------|-------------|-------------|
|
|
239
|
+
| `connect()` | `Promise<Connector \| undefined>` | Establish database connection. Useful for connection pooling. |
|
|
240
|
+
| `disconnect()` | `Promise<void>` | Close database connection gracefully. |
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
@datasource({ driver: 'node-postgres' })
|
|
244
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
245
|
+
// ... constructor and configure() ...
|
|
246
|
+
|
|
247
|
+
override async connect(): Promise<TNodePostgresConnector | undefined> {
|
|
248
|
+
await (this.connector.client as Pool).connect();
|
|
249
|
+
return this.connector;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
override async disconnect(): Promise<void> {
|
|
253
|
+
await (this.connector.client as Pool).end();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Helper Methods
|
|
259
|
+
|
|
260
|
+
| Method | Description |
|
|
261
|
+
|--------|-------------|
|
|
262
|
+
| `getSchema()` | Returns the schema (auto-discovers if not manually provided) |
|
|
263
|
+
| `getSettings()` | Returns connection settings |
|
|
264
|
+
| `getConnector()` | Returns the Drizzle connector |
|
|
265
|
+
| `hasDiscoverableModels()` | Returns `true` if there are models registered for this datasource |
|
|
266
|
+
|
|
105
267
|
This architecture ensures that datasources are configured consistently and that the fully-initialized Drizzle connector, aware of all schemas and relations, is available to repositories for querying.
|
|
@@ -4,24 +4,25 @@ Technical reference for the DI system in Ignis - managing resource lifecycles an
|
|
|
4
4
|
|
|
5
5
|
**Files:**
|
|
6
6
|
- `packages/inversion/src/container.ts` (base Container and Binding classes)
|
|
7
|
-
- `packages/
|
|
7
|
+
- `packages/core/src/helpers/inversion/container.ts` (extended Container with ApplicationLogger)
|
|
8
8
|
- `packages/core/src/base/metadata/injectors.ts` (@inject, @injectable decorators)
|
|
9
|
-
- `packages/
|
|
9
|
+
- `packages/core/src/helpers/inversion/registry.ts` (MetadataRegistry)
|
|
10
10
|
|
|
11
11
|
## Quick Reference
|
|
12
12
|
|
|
13
13
|
| Component | Purpose | Key Methods |
|
|
14
14
|
|-----------|---------|-------------|
|
|
15
15
|
| **Container** | DI registry managing resource lifecycles | `bind()`, `get()`, `instantiate()`, `findByTag()` |
|
|
16
|
-
| **Binding** | Single registered dependency configuration | `toClass()`, `toValue()`, `toProvider()`, `setScope()` |
|
|
16
|
+
| **Binding** | Single registered dependency configuration | `toClass()`, `toValue()`, `toProvider()`, `setScope()`, `setTags()` |
|
|
17
17
|
| **@inject** | Decorator marking injection points | Applied to constructor parameters/properties |
|
|
18
18
|
| **MetadataRegistry** | Stores decorator metadata | Singleton accessed via `getInstance()` |
|
|
19
|
+
| **Boot System** | Automatic artifact discovery and binding | Integrates with Container via tags and bindings |
|
|
19
20
|
|
|
20
21
|
## `Container` Class
|
|
21
22
|
|
|
22
23
|
Heart of the DI system - registry managing all application resources.
|
|
23
24
|
|
|
24
|
-
**File:** `packages/inversion/src/container.ts`
|
|
25
|
+
**File:** `packages/inversion/src/container.ts` (Base) & `packages/core/src/helpers/inversion/container.ts` (Extended)
|
|
25
26
|
|
|
26
27
|
### Key Methods
|
|
27
28
|
|
|
@@ -74,7 +75,7 @@ This entire process is managed by the framework when your application starts up,
|
|
|
74
75
|
|
|
75
76
|
The `MetadataRegistry` is a crucial part of the DI and routing systems. It's a singleton class responsible for storing and retrieving all the metadata attached by decorators like `@inject`, `@controller`, `@get`, etc.
|
|
76
77
|
|
|
77
|
-
- **File:** `packages/
|
|
78
|
+
- **File:** `packages/core/src/helpers/inversion/registry.ts`
|
|
78
79
|
|
|
79
80
|
### Role in DI
|
|
80
81
|
|
|
@@ -82,3 +83,95 @@ The `MetadataRegistry` is a crucial part of the DI and routing systems. It's a s
|
|
|
82
83
|
- When the `Container` instantiates a class, it queries the `MetadataRegistry` to find out which dependencies need to be injected and where.
|
|
83
84
|
|
|
84
85
|
You typically won't interact with the `MetadataRegistry` directly, but it's the underlying mechanism that makes the decorator-based DI and routing systems work seamlessly.
|
|
86
|
+
|
|
87
|
+
## Boot System Integration
|
|
88
|
+
|
|
89
|
+
The boot system (`@venizia/ignis-boot`) extends the DI container to support automatic artifact discovery and registration.
|
|
90
|
+
|
|
91
|
+
### Key Bindings
|
|
92
|
+
|
|
93
|
+
When boot system is enabled, the following bindings are created:
|
|
94
|
+
|
|
95
|
+
| Binding Key | Type | Description |
|
|
96
|
+
|-------------|------|-------------|
|
|
97
|
+
| `@app/instance` | Value | The application container instance |
|
|
98
|
+
| `@app/project_root` | Value | Absolute path to project root |
|
|
99
|
+
| `@app/boot-options` | Value | Boot configuration options |
|
|
100
|
+
| `bootstrapper` | Class (Singleton) | Main boot orchestrator |
|
|
101
|
+
| `booter.DatasourceBooter` | Class (Tagged: 'booter') | Datasource discovery booter |
|
|
102
|
+
| `booter.RepositoryBooter` | Class (Tagged: 'booter') | Repository discovery booter |
|
|
103
|
+
| `booter.ServiceBooter` | Class (Tagged: 'booter') | Service discovery booter |
|
|
104
|
+
| `booter.ControllerBooter` | Class (Tagged: 'booter') | Controller discovery booter |
|
|
105
|
+
|
|
106
|
+
### Tag-based Discovery
|
|
107
|
+
|
|
108
|
+
The boot system uses container tags for automatic discovery:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Register a booter with tag
|
|
112
|
+
this.bind({ key: 'booter.CustomBooter' })
|
|
113
|
+
.toClass(CustomBooter)
|
|
114
|
+
.setTags('booter');
|
|
115
|
+
|
|
116
|
+
// Find all booters
|
|
117
|
+
const booterBindings = this.findByTag<IBooter>({ tag: 'booter' });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This pattern allows the `Bootstrapper` to automatically discover and execute all registered booters without explicit registration.
|
|
121
|
+
|
|
122
|
+
### Artifact Bindings
|
|
123
|
+
|
|
124
|
+
Once artifacts are discovered and loaded, they're bound using consistent patterns:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// Controllers
|
|
128
|
+
this.bind({ key: 'controllers.UserController' }).toClass(UserController);
|
|
129
|
+
|
|
130
|
+
// Services
|
|
131
|
+
this.bind({ key: 'services.UserService' }).toClass(UserService);
|
|
132
|
+
|
|
133
|
+
// Repositories
|
|
134
|
+
this.bind({ key: 'repositories.UserRepository' }).toClass(UserRepository);
|
|
135
|
+
|
|
136
|
+
// Datasources
|
|
137
|
+
this.bind({ key: 'datasources.PostgresDataSource' }).toClass(PostgresDataSource);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Boot Lifecycle & DI
|
|
141
|
+
|
|
142
|
+
The boot system integrates into the application lifecycle:
|
|
143
|
+
|
|
144
|
+
1. **Application Constructor** - Binds boot infrastructure if `bootOptions` configured
|
|
145
|
+
2. **initialize()** - Calls `boot()` which:
|
|
146
|
+
- Discovers booters from container (via `findByTag`)
|
|
147
|
+
- Instantiates booters (via `container.get()` or `binding.getValue()`)
|
|
148
|
+
- Executes boot phases (configure → discover → load)
|
|
149
|
+
- Each booter binds discovered artifacts to container
|
|
150
|
+
3. **Post-Boot** - All artifacts available for dependency injection
|
|
151
|
+
|
|
152
|
+
**Example Flow:**
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// 1. Boot discovers UserController.js file
|
|
156
|
+
// 2. Boot loads UserController class
|
|
157
|
+
// 3. Boot binds to container:
|
|
158
|
+
app.bind({ key: 'controllers.UserController' }).toClass(UserController);
|
|
159
|
+
|
|
160
|
+
// 4. Later, when UserController is instantiated:
|
|
161
|
+
@injectable()
|
|
162
|
+
class UserController {
|
|
163
|
+
constructor(
|
|
164
|
+
@inject({ key: 'services.UserService' })
|
|
165
|
+
private userService: UserService // Auto-injected!
|
|
166
|
+
) {}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Benefits
|
|
171
|
+
|
|
172
|
+
- **Zero-configuration DI**: Artifacts auto-discovered and registered
|
|
173
|
+
- **Convention-based**: Follow naming patterns, get DI for free
|
|
174
|
+
- **Extensible**: Custom booters integrate seamlessly via tags
|
|
175
|
+
- **Type-safe**: Full TypeScript support throughout boot process
|
|
176
|
+
|
|
177
|
+
> **Learn More:** See [Bootstrapping Concepts](/get-started/core-concepts/bootstrapping.md) and [Boot Package Reference](/references/src-details/boot.md)
|