@venizia/ignis-docs 0.0.3 → 0.0.4-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/README.md +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +6 -0
- package/wiki/changelogs/planned-schema-migrator.md +0 -8
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +31 -26
- package/wiki/references/base/components.md +24 -7
- package/wiki/references/base/controllers.md +50 -20
- package/wiki/references/base/datasources.md +30 -0
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +604 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +731 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -635
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +14 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +16 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -97
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/persistent.md +0 -539
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- package/wiki/get-started/prerequisites.md +0 -113
|
@@ -15,16 +15,7 @@ A single component can bundle everything needed for a specific domain—for exam
|
|
|
15
15
|
|
|
16
16
|
## Built-in Components
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
| Component | Description |
|
|
21
|
-
|-----------|-------------|
|
|
22
|
-
| `AuthenticateComponent` | JWT-based authentication with token services |
|
|
23
|
-
| `SwaggerComponent` | Interactive OpenAPI documentation |
|
|
24
|
-
| `HealthCheckComponent` | Health check endpoints |
|
|
25
|
-
| `RequestTrackerComponent` | Request logging and tracing |
|
|
26
|
-
| `SocketIOComponent` | Real-time communication with Socket.IO |
|
|
27
|
-
| `StaticAssetComponent` | File upload and storage management |
|
|
18
|
+
Ignis includes ready-to-use components for common features like authentication, API documentation, health checks, and more. See the [**Built-in Components Reference**](../../references/components/) for the complete list with detailed documentation.
|
|
28
19
|
|
|
29
20
|
## Creating a Simple Component
|
|
30
21
|
|
|
@@ -33,7 +24,7 @@ import { BaseApplication, BaseComponent, inject, CoreBindings, ValueOrPromise, B
|
|
|
33
24
|
|
|
34
25
|
// Define a service
|
|
35
26
|
class NotificationService {
|
|
36
|
-
send(message: string) { /* ... */ }
|
|
27
|
+
send(opts: { message: string }) { /* ... */ }
|
|
37
28
|
}
|
|
38
29
|
|
|
39
30
|
// Define a controller
|
|
@@ -41,7 +32,7 @@ class NotificationService {
|
|
|
41
32
|
class NotificationController extends BaseController {
|
|
42
33
|
constructor(
|
|
43
34
|
@inject({ key: 'services.NotificationService' })
|
|
44
|
-
private
|
|
35
|
+
private _notificationService: NotificationService
|
|
45
36
|
) {
|
|
46
37
|
super({ scope: NotificationController.name });
|
|
47
38
|
}
|
|
@@ -51,11 +42,11 @@ class NotificationController extends BaseController {
|
|
|
51
42
|
export class NotificationComponent extends BaseComponent {
|
|
52
43
|
constructor(
|
|
53
44
|
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
54
|
-
private
|
|
45
|
+
private _application: BaseApplication,
|
|
55
46
|
) {
|
|
56
47
|
super({
|
|
57
48
|
scope: NotificationComponent.name,
|
|
58
|
-
initDefault: { enable: true, container:
|
|
49
|
+
initDefault: { enable: true, container: _application },
|
|
59
50
|
bindings: {
|
|
60
51
|
'services.NotificationService': Binding.bind({ key: 'services.NotificationService' })
|
|
61
52
|
.toClass(NotificationService),
|
|
@@ -64,7 +55,7 @@ export class NotificationComponent extends BaseComponent {
|
|
|
64
55
|
}
|
|
65
56
|
|
|
66
57
|
override binding(): ValueOrPromise<void> {
|
|
67
|
-
this.
|
|
58
|
+
this._application.controller(NotificationController);
|
|
68
59
|
}
|
|
69
60
|
}
|
|
70
61
|
```
|
|
@@ -108,8 +99,24 @@ export class Application extends BaseApplication {
|
|
|
108
99
|
}
|
|
109
100
|
```
|
|
110
101
|
|
|
111
|
-
---
|
|
112
|
-
|
|
113
102
|
> **Next Steps:**
|
|
103
|
+
> - [Creating Components Guide](./components-guide.md) - Step-by-step tutorial for building your own components
|
|
114
104
|
> - [Components Reference](../../references/base/components.md) - Directory structure, keys, types, constants patterns
|
|
115
105
|
> - [Built-in Components](../../references/components/) - Detailed documentation for each component
|
|
106
|
+
|
|
107
|
+
## See Also
|
|
108
|
+
|
|
109
|
+
- **Related Concepts:**
|
|
110
|
+
- [Application](/guides/core-concepts/application/) - Registering components
|
|
111
|
+
- [Dependency Injection](/guides/core-concepts/dependency-injection) - Component bindings
|
|
112
|
+
- [Creating Components](./components-guide) - Build your own components
|
|
113
|
+
|
|
114
|
+
- **References:**
|
|
115
|
+
- [BaseComponent API](/references/base/components) - Complete API reference
|
|
116
|
+
- [Authentication Component](/references/components/authentication) - JWT authentication
|
|
117
|
+
- [Health Check Component](/references/components/health-check) - Health endpoints
|
|
118
|
+
- [Swagger Component](/references/components/swagger) - API documentation
|
|
119
|
+
- [Socket.IO Component](/references/components/socket-io) - WebSocket support
|
|
120
|
+
|
|
121
|
+
- **Best Practices:**
|
|
122
|
+
- [Architectural Patterns](/best-practices/architectural-patterns) - Component design patterns
|
|
@@ -74,8 +74,8 @@ import { BaseController, controller, get, post, HTTP, jsonContent, jsonResponse,
|
|
|
74
74
|
import { z } from '@hono/zod-openapi';
|
|
75
75
|
|
|
76
76
|
// Define route configs as const for type inference
|
|
77
|
-
const
|
|
78
|
-
|
|
77
|
+
const TestRoutes = {
|
|
78
|
+
GET_DATA: {
|
|
79
79
|
method: HTTP.Methods.GET,
|
|
80
80
|
path: '/',
|
|
81
81
|
responses: jsonResponse({
|
|
@@ -83,7 +83,7 @@ const TEST_ROUTES = {
|
|
|
83
83
|
schema: z.object({ message: z.string(), method: z.string() }),
|
|
84
84
|
}),
|
|
85
85
|
},
|
|
86
|
-
|
|
86
|
+
CREATE_ITEM: {
|
|
87
87
|
method: HTTP.Methods.POST,
|
|
88
88
|
path: '/',
|
|
89
89
|
request: {
|
|
@@ -105,18 +105,18 @@ export class MyItemsController extends BaseController {
|
|
|
105
105
|
super({ scope: MyItemsController.name, path: '/my-items' });
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
@get({ configs:
|
|
109
|
-
getData(c: TRouteContext<typeof
|
|
108
|
+
@get({ configs: TestRoutes.GET_DATA })
|
|
109
|
+
getData(c: TRouteContext<typeof TestRoutes.GET_DATA>) { // Return type is automatically inferred
|
|
110
110
|
// 'c' is fully typed here, including c.req.valid and c.json return type
|
|
111
111
|
return c.json({ message: 'Hello from decorator', method: 'GET' }, HTTP.ResultCodes.RS_2.Ok);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
@post({ configs:
|
|
115
|
-
createItem(c: TRouteContext<typeof
|
|
116
|
-
// c.req.valid('json') is automatically typed based on
|
|
114
|
+
@post({ configs: TestRoutes.CREATE_ITEM })
|
|
115
|
+
createItem(c: TRouteContext<typeof TestRoutes.CREATE_ITEM>) { // Return type is automatically inferred
|
|
116
|
+
// c.req.valid('json') is automatically typed based on CREATE_ITEM.request.body.content['application/json'].schema
|
|
117
117
|
const body = c.req.valid('json');
|
|
118
118
|
|
|
119
|
-
// Return type is automatically validated against
|
|
119
|
+
// Return type is automatically validated against CREATE_ITEM.responses[200].content['application/json'].schema
|
|
120
120
|
return c.json(
|
|
121
121
|
{
|
|
122
122
|
id: 'some-uuid',
|
|
@@ -204,7 +204,7 @@ this.bindRoute({
|
|
|
204
204
|
configs: GetUserByIdRoute,
|
|
205
205
|
}).to({
|
|
206
206
|
handler: (c: TRouteContext<typeof GetUserByIdRoute>) => { // Return type is automatically inferred
|
|
207
|
-
const { id } = c.req.param()
|
|
207
|
+
const { id } = c.req.valid('param'); // Use valid() for type-safe validated params
|
|
208
208
|
return c.json({ id: id, name: 'John Doe' }, HTTP.ResultCodes.RS_2.Ok);
|
|
209
209
|
},
|
|
210
210
|
});
|
|
@@ -409,7 +409,7 @@ import { jsonContent, put, TRouteContext, HTTP } from '@venizia/ignis';
|
|
|
409
409
|
|
|
410
410
|
// ... inside a controller class
|
|
411
411
|
|
|
412
|
-
const
|
|
412
|
+
const UpdateUserConfig = {
|
|
413
413
|
path: '/:id',
|
|
414
414
|
method: 'put',
|
|
415
415
|
request: {
|
|
@@ -422,8 +422,8 @@ const updateUserConfig = {
|
|
|
422
422
|
// ... responses
|
|
423
423
|
} as const; // Use 'as const' for strict type inference
|
|
424
424
|
|
|
425
|
-
@put({ configs:
|
|
426
|
-
updateUser(c: TRouteContext<typeof
|
|
425
|
+
@put({ configs: UpdateUserConfig })
|
|
426
|
+
updateUser(c: TRouteContext<typeof UpdateUserConfig>) {
|
|
427
427
|
// Access validated data from the request
|
|
428
428
|
const { id } = c.req.valid('param');
|
|
429
429
|
const { notify } = c.req.valid('query');
|
|
@@ -439,3 +439,20 @@ updateUser(c: TRouteContext<typeof updateUserConfig>) {
|
|
|
439
439
|
```
|
|
440
440
|
|
|
441
441
|
Using `TRouteContext` provides full type inference for `c.req.valid()`, so your editor will know that `id` is a string, `notify` is an optional string, and `userUpdateData` matches the body schema.
|
|
442
|
+
|
|
443
|
+
## See Also
|
|
444
|
+
|
|
445
|
+
- **Related Concepts:**
|
|
446
|
+
- [Application](/guides/core-concepts/application/) - Registering controllers
|
|
447
|
+
- [Services](/guides/core-concepts/services) - Business logic layer called by controllers
|
|
448
|
+
- [Dependency Injection](/guides/core-concepts/dependency-injection) - Injecting services into controllers
|
|
449
|
+
|
|
450
|
+
- **References:**
|
|
451
|
+
- [BaseController API](/references/base/controllers) - Complete API reference
|
|
452
|
+
- [Middlewares](/references/base/middlewares) - Request interceptors
|
|
453
|
+
- [Swagger Component](/references/components/swagger) - Auto-generate API docs
|
|
454
|
+
- [Schema Utilities](/references/utilities/schema) - Request/response helpers
|
|
455
|
+
|
|
456
|
+
- **Tutorials:**
|
|
457
|
+
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Controller examples
|
|
458
|
+
- [E-commerce API](/guides/tutorials/ecommerce-api) - Advanced routing patterns
|
|
@@ -170,3 +170,100 @@ You would then bind this provider in your application:
|
|
|
170
170
|
this.bind<ThirdPartyApiClient>({ key: 'services.ApiClient' })
|
|
171
171
|
.toProvider(ApiClientProvider);
|
|
172
172
|
```
|
|
173
|
+
|
|
174
|
+
## Standalone Containers
|
|
175
|
+
|
|
176
|
+
You can create independent DI containers using the `Container` class directly. These containers are **completely separate** from the application's context and do not share any bindings.
|
|
177
|
+
|
|
178
|
+
### Creating an Independent Container
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { Container, BindingScopes } from '@venizia/ignis-inversion';
|
|
182
|
+
|
|
183
|
+
// Create a standalone container
|
|
184
|
+
const container = new Container({ scope: 'MyCustomContainer' });
|
|
185
|
+
|
|
186
|
+
// Bind dependencies
|
|
187
|
+
container.bind({ key: 'config.apiKey' }).toValue('my-secret-key');
|
|
188
|
+
container.bind({ key: 'services.Logger' }).toClass(LoggerService);
|
|
189
|
+
container.bind({ key: 'services.Cache' })
|
|
190
|
+
.toClass(CacheService)
|
|
191
|
+
.setScope(BindingScopes.SINGLETON);
|
|
192
|
+
|
|
193
|
+
// Resolve dependencies
|
|
194
|
+
const logger = container.get<LoggerService>({ key: 'services.Logger' });
|
|
195
|
+
const apiKey = container.getSync<string>({ key: 'config.apiKey' });
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Use Cases
|
|
199
|
+
|
|
200
|
+
| Use Case | Description |
|
|
201
|
+
|----------|-------------|
|
|
202
|
+
| **Unit Testing** | Create isolated containers with mock dependencies for each test |
|
|
203
|
+
| **Isolated Modules** | Build self-contained modules with their own dependency graph |
|
|
204
|
+
| **Multi-Tenancy** | Separate containers per tenant with tenant-specific configurations |
|
|
205
|
+
| **Worker Threads** | Independent containers for background workers |
|
|
206
|
+
| **Plugin Systems** | Each plugin gets its own container to prevent conflicts |
|
|
207
|
+
|
|
208
|
+
### Example: Testing with Isolated Container
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { Container } from '@venizia/ignis-inversion';
|
|
212
|
+
import { describe, it, expect, beforeEach } from 'bun:test';
|
|
213
|
+
|
|
214
|
+
describe('UserService', () => {
|
|
215
|
+
let container: Container;
|
|
216
|
+
|
|
217
|
+
beforeEach(() => {
|
|
218
|
+
// Fresh container for each test
|
|
219
|
+
container = new Container({ scope: 'TestContainer' });
|
|
220
|
+
|
|
221
|
+
// Bind mock dependencies
|
|
222
|
+
container.bind({ key: 'repositories.UserRepository' }).toValue({
|
|
223
|
+
findById: async () => ({ id: '1', name: 'Test User' }),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
container.bind({ key: 'services.UserService' }).toClass(UserService);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should find user by id', async () => {
|
|
230
|
+
const userService = container.get<UserService>({ key: 'services.UserService' });
|
|
231
|
+
const user = await userService.findById({ id: '1' });
|
|
232
|
+
|
|
233
|
+
expect(user.name).toBe('Test User');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Container vs Application
|
|
239
|
+
|
|
240
|
+
| Aspect | `Application` (extends Container) | Standalone `Container` |
|
|
241
|
+
|--------|-----------------------------------|------------------------|
|
|
242
|
+
| **Purpose** | Full HTTP server with routing, middleware | Pure dependency injection |
|
|
243
|
+
| **Bindings** | Shared across entire application | Isolated, no sharing |
|
|
244
|
+
| **Lifecycle** | Managed by framework | You control it |
|
|
245
|
+
| **Use Case** | Main application | Testing, isolated modules, workers |
|
|
246
|
+
|
|
247
|
+
::: tip
|
|
248
|
+
The `Application` class extends `Container`, so all container methods (`bind`, `get`, `getSync`) are available on your application instance. Standalone containers are useful when you need isolation from the main application context.
|
|
249
|
+
:::
|
|
250
|
+
|
|
251
|
+
## See Also
|
|
252
|
+
|
|
253
|
+
- **Related Concepts:**
|
|
254
|
+
- [Application](/guides/core-concepts/application/) - Application extends Container
|
|
255
|
+
- [Controllers](/guides/core-concepts/controllers) - Use DI for injecting services
|
|
256
|
+
- [Services](/guides/core-concepts/services) - Use DI for injecting repositories
|
|
257
|
+
- [Providers](/references/base/providers) - Factory pattern for dynamic injection
|
|
258
|
+
|
|
259
|
+
- **References:**
|
|
260
|
+
- [Dependency Injection API](/references/base/dependency-injection) - Complete API reference
|
|
261
|
+
- [Inversion Helper](/references/helpers/inversion) - DI container utilities
|
|
262
|
+
- [Glossary](/guides/reference/glossary#dependency-injection-di) - DI concepts explained
|
|
263
|
+
|
|
264
|
+
- **Tutorials:**
|
|
265
|
+
- [Testing](/guides/tutorials/testing) - Unit testing with mocked dependencies
|
|
266
|
+
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - DI in practice
|
|
267
|
+
|
|
268
|
+
- **Best Practices:**
|
|
269
|
+
- [Architectural Patterns](/best-practices/architectural-patterns) - DI patterns and anti-patterns
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# DataSources
|
|
2
|
+
|
|
3
|
+
A DataSource manages database connections and supports **schema auto-discovery** from repositories.
|
|
4
|
+
|
|
5
|
+
::: info PostgreSQL First
|
|
6
|
+
IGNIS currently focuses on **PostgreSQL** as the primary database. Support for other database systems (MySQL, SQLite, etc.) is planned for future releases.
|
|
7
|
+
:::
|
|
8
|
+
|
|
9
|
+
## Creating a DataSource
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// src/datasources/postgres.datasource.ts
|
|
13
|
+
import {
|
|
14
|
+
BaseDataSource,
|
|
15
|
+
datasource,
|
|
16
|
+
TNodePostgresConnector,
|
|
17
|
+
ValueOrPromise,
|
|
18
|
+
} from '@venizia/ignis';
|
|
19
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
20
|
+
import { Pool } from 'pg';
|
|
21
|
+
|
|
22
|
+
interface IDSConfigs {
|
|
23
|
+
host: string;
|
|
24
|
+
port: number;
|
|
25
|
+
database: string;
|
|
26
|
+
user: string;
|
|
27
|
+
password: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@datasource({ driver: 'node-postgres' })
|
|
31
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
32
|
+
constructor() {
|
|
33
|
+
super({
|
|
34
|
+
name: PostgresDataSource.name,
|
|
35
|
+
config: {
|
|
36
|
+
host: process.env.POSTGRES_HOST ?? 'localhost',
|
|
37
|
+
port: +(process.env.POSTGRES_PORT ?? 5432),
|
|
38
|
+
database: process.env.POSTGRES_DATABASE ?? 'mydb',
|
|
39
|
+
user: process.env.POSTGRES_USER ?? 'postgres',
|
|
40
|
+
password: process.env.POSTGRES_PASSWORD ?? '',
|
|
41
|
+
},
|
|
42
|
+
// No schema needed - auto-discovered from @repository bindings!
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override configure(): ValueOrPromise<void> {
|
|
47
|
+
// getSchema() auto-discovers models from @repository bindings
|
|
48
|
+
const schema = this.getSchema();
|
|
49
|
+
|
|
50
|
+
this.logger.debug(
|
|
51
|
+
'[configure] Auto-discovered schema | Keys: %o',
|
|
52
|
+
Object.keys(schema),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
this.pool = new Pool(this.settings);
|
|
56
|
+
this.connector = drizzle({ client: this.pool, schema });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
override getConnectionString(): ValueOrPromise<string> {
|
|
60
|
+
const { host, port, user, password, database } = this.settings;
|
|
61
|
+
return `postgresql://${user}:${password}@${host}:${port}/${database}`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**How auto-discovery works:**
|
|
67
|
+
|
|
68
|
+
1. `@repository` decorators register model-datasource bindings
|
|
69
|
+
2. When `configure()` is called, `getSchema()` collects all bound models
|
|
70
|
+
3. Drizzle is initialized with the complete schema
|
|
71
|
+
|
|
72
|
+
## Manual Schema (Optional)
|
|
73
|
+
|
|
74
|
+
If you need explicit control, you can still provide schema manually:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
@datasource({ driver: 'node-postgres' })
|
|
78
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
79
|
+
constructor() {
|
|
80
|
+
super({
|
|
81
|
+
name: PostgresDataSource.name,
|
|
82
|
+
config: { /* ... */ },
|
|
83
|
+
schema: {
|
|
84
|
+
User: User.schema,
|
|
85
|
+
Configuration: Configuration.schema,
|
|
86
|
+
// Add relations if using Drizzle's relational queries
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Registering a DataSource
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// src/application.ts
|
|
97
|
+
export class Application extends BaseApplication {
|
|
98
|
+
preConfigure(): ValueOrPromise<void> {
|
|
99
|
+
this.dataSource(PostgresDataSource);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Supported Drivers
|
|
105
|
+
|
|
106
|
+
| Driver | Package | Status |
|
|
107
|
+
|--------|---------|--------|
|
|
108
|
+
| `node-postgres` | `pg` | ✅ Supported |
|
|
109
|
+
| `mysql2` | `mysql2` | 🔜 Planned |
|
|
110
|
+
| `better-sqlite3` | `better-sqlite3` | 🔜 Planned |
|
|
111
|
+
|
|
112
|
+
## DataSource Template
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { BaseDataSource, datasource, TNodePostgresConnector, ValueOrPromise } from '@venizia/ignis';
|
|
116
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
117
|
+
import { Pool } from 'pg';
|
|
118
|
+
|
|
119
|
+
interface IDSConfigs {
|
|
120
|
+
host: string;
|
|
121
|
+
port: number;
|
|
122
|
+
database: string;
|
|
123
|
+
user: string;
|
|
124
|
+
password: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@datasource({ driver: 'node-postgres' })
|
|
128
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
129
|
+
constructor() {
|
|
130
|
+
super({
|
|
131
|
+
name: PostgresDataSource.name,
|
|
132
|
+
config: {
|
|
133
|
+
host: process.env.POSTGRES_HOST ?? 'localhost',
|
|
134
|
+
port: +(process.env.POSTGRES_PORT ?? 5432),
|
|
135
|
+
database: process.env.POSTGRES_DATABASE ?? 'mydb',
|
|
136
|
+
user: process.env.POSTGRES_USER ?? 'postgres',
|
|
137
|
+
password: process.env.POSTGRES_PASSWORD ?? '',
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override configure(): ValueOrPromise<void> {
|
|
143
|
+
const schema = this.getSchema();
|
|
144
|
+
this.pool = new Pool(this.settings);
|
|
145
|
+
this.connector = drizzle({ client: this.pool, schema });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
override getConnectionString(): ValueOrPromise<string> {
|
|
149
|
+
const { host, port, user, password, database } = this.settings;
|
|
150
|
+
return `postgresql://${user}:${password}@${host}:${port}/${database}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
> **Deep Dive:** See [BaseDataSource Reference](../../../references/base/datasources.md) for connection pooling and advanced configuration.
|
|
156
|
+
|
|
157
|
+
## See Also
|
|
158
|
+
|
|
159
|
+
- **Related Concepts:**
|
|
160
|
+
- [Repositories](/guides/core-concepts/persistent/repositories) - Use DataSources for database access
|
|
161
|
+
- [Models](/guides/core-concepts/persistent/models) - Entity schemas loaded by DataSource
|
|
162
|
+
- [Transactions](/guides/core-concepts/persistent/transactions) - Multi-operation database transactions
|
|
163
|
+
- [Application](/guides/core-concepts/application/) - Registering DataSources
|
|
164
|
+
|
|
165
|
+
- **References:**
|
|
166
|
+
- [BaseDataSource API](/references/base/datasources) - Complete API reference
|
|
167
|
+
- [Environment Variables](/references/configuration/environment-variables) - Configuration management
|
|
168
|
+
|
|
169
|
+
- **External Resources:**
|
|
170
|
+
- [Drizzle ORM Documentation](https://orm.drizzle.team/) - ORM configuration
|
|
171
|
+
- [node-postgres Documentation](https://node-postgres.com/) - Connection pooling guide
|
|
172
|
+
|
|
173
|
+
- **Best Practices:**
|
|
174
|
+
- [Performance Optimization](/best-practices/performance-optimization) - Connection pool tuning
|
|
175
|
+
- [Security Guidelines](/best-practices/security-guidelines) - Database credential management
|
|
176
|
+
|
|
177
|
+
- **Tutorials:**
|
|
178
|
+
- [Complete Installation](/guides/tutorials/complete-installation) - Database setup
|
|
179
|
+
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - DataSource configuration
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Persistent Layer
|
|
2
|
+
|
|
3
|
+
The persistent layer manages data using [Drizzle ORM](https://orm.drizzle.team/) for type-safe database access and the Repository pattern for data abstraction.
|
|
4
|
+
|
|
5
|
+
## Architecture Overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────────────┐
|
|
9
|
+
│ Application │
|
|
10
|
+
├─────────────────────────────────────────────────────────┤
|
|
11
|
+
│ Controllers → Services → Repositories → Database │
|
|
12
|
+
└─────────────────────────────────────────────────────────┘
|
|
13
|
+
▲
|
|
14
|
+
│
|
|
15
|
+
┌────────────────┴────────────────┐
|
|
16
|
+
│ │
|
|
17
|
+
┌─────┴─────┐ ┌───────┴───────┐
|
|
18
|
+
│ Models │ │ DataSources │
|
|
19
|
+
│ (Schema) │ │ (Connection) │
|
|
20
|
+
└───────────┘ └───────────────┘
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Core Components
|
|
24
|
+
|
|
25
|
+
| Component | Description | Learn More |
|
|
26
|
+
|-----------|-------------|------------|
|
|
27
|
+
| **Models** | Define data structure with Drizzle schemas and relations | [Models Guide](./models.md) |
|
|
28
|
+
| **DataSources** | Manage database connections with auto-discovery | [DataSources Guide](./datasources.md) |
|
|
29
|
+
| **Repositories** | Provide type-safe CRUD operations | [Repositories Guide](./repositories.md) |
|
|
30
|
+
| **Transactions** | Handle atomic multi-step operations | [Transactions Guide](./transactions.md) |
|
|
31
|
+
|
|
32
|
+
## Quick Example
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// 1. Define a Model
|
|
36
|
+
@model({ type: 'entity' })
|
|
37
|
+
export class User extends BaseEntity<typeof User.schema> {
|
|
38
|
+
static override schema = pgTable('User', {
|
|
39
|
+
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
40
|
+
name: text('name').notNull(),
|
|
41
|
+
email: text('email').notNull(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
static override relations = () => [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 2. Create a DataSource
|
|
48
|
+
@datasource({ driver: 'node-postgres' })
|
|
49
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
50
|
+
constructor() {
|
|
51
|
+
super({
|
|
52
|
+
name: PostgresDataSource.name,
|
|
53
|
+
config: {
|
|
54
|
+
host: process.env.POSTGRES_HOST ?? 'localhost',
|
|
55
|
+
port: +(process.env.POSTGRES_PORT ?? 5432),
|
|
56
|
+
database: process.env.POSTGRES_DATABASE ?? 'mydb',
|
|
57
|
+
user: process.env.POSTGRES_USER ?? 'postgres',
|
|
58
|
+
password: process.env.POSTGRES_PASSWORD ?? '',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override configure(): ValueOrPromise<void> {
|
|
64
|
+
const schema = this.getSchema();
|
|
65
|
+
this.pool = new Pool(this.settings);
|
|
66
|
+
this.connector = drizzle({ client: this.pool, schema });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 3. Create a Repository
|
|
71
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
72
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
73
|
+
async findByEmail(opts: { email: string }) {
|
|
74
|
+
return this.findOne({ filter: { where: { email: opts.email } } });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 4. Use in Application
|
|
79
|
+
export class Application extends BaseApplication {
|
|
80
|
+
preConfigure() {
|
|
81
|
+
this.dataSource(PostgresDataSource);
|
|
82
|
+
this.repository(UserRepository);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Next Steps
|
|
88
|
+
|
|
89
|
+
1. **[Models](./models.md)** - Learn how to define your data structure
|
|
90
|
+
2. **[DataSources](./datasources.md)** - Configure database connections
|
|
91
|
+
3. **[Repositories](./repositories.md)** - Master CRUD operations and queries
|
|
92
|
+
4. **[Transactions](./transactions.md)** - Handle atomic operations
|
|
93
|
+
|
|
94
|
+
> **Deep Dive:** See [Repository Reference](../../../references/base/repositories/) for advanced filtering, relations, and operators.
|
|
95
|
+
|
|
96
|
+
## See Also
|
|
97
|
+
|
|
98
|
+
- **Persistent Layer Topics:**
|
|
99
|
+
- [Models](./models) - Entity definitions and schemas
|
|
100
|
+
- [DataSources](./datasources) - Database connections
|
|
101
|
+
- [Repositories](./repositories) - Data access layer
|
|
102
|
+
- [Transactions](./transactions) - Atomic operations
|
|
103
|
+
|
|
104
|
+
- **Related Concepts:**
|
|
105
|
+
- [Services](/guides/core-concepts/services) - Use repositories for business logic
|
|
106
|
+
- [Application](/guides/core-concepts/application/) - Registering persistent resources
|
|
107
|
+
|
|
108
|
+
- **References:**
|
|
109
|
+
- [Models API](/references/base/models) - Complete models reference
|
|
110
|
+
- [DataSources API](/references/base/datasources) - Complete datasources reference
|
|
111
|
+
- [Repositories API](/references/base/repositories/) - Complete repositories reference
|
|
112
|
+
- [Filter System](/references/base/filter-system/) - Query operators
|
|
113
|
+
|
|
114
|
+
- **External Resources:**
|
|
115
|
+
- [Drizzle ORM Documentation](https://orm.drizzle.team/) - ORM guide
|
|
116
|
+
|
|
117
|
+
- **Tutorials:**
|
|
118
|
+
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Complete persistence example
|
|
119
|
+
- [E-commerce API](/guides/tutorials/ecommerce-api) - Advanced persistence patterns
|