@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
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
# API Usage Examples
|
|
2
|
-
|
|
3
|
-
Practical examples for defining endpoints and working with data in Ignis applications.
|
|
4
|
-
|
|
5
|
-
## Routing Patterns
|
|
6
|
-
|
|
7
|
-
### Decorator-Based Routing (Recommended)
|
|
8
|
-
|
|
9
|
-
Use `@get`, `@post` decorators with `as const` route configs for full type safety:
|
|
10
|
-
|
|
11
|
-
**`src/controllers/test/definitions.ts`**
|
|
12
|
-
```typescript
|
|
13
|
-
import { z } from '@hono/zod-openapi';
|
|
14
|
-
import { Authentication, HTTP, jsonContent, jsonResponse } from '@venizia/ignis';
|
|
15
|
-
|
|
16
|
-
// Define route configs as const for type inference
|
|
17
|
-
export const ROUTE_CONFIGS = {
|
|
18
|
-
// ... (other routes)
|
|
19
|
-
['/4']: {
|
|
20
|
-
method: HTTP.Methods.GET,
|
|
21
|
-
path: '/4',
|
|
22
|
-
responses: jsonResponse({
|
|
23
|
-
description: 'Test decorator GET endpoint',
|
|
24
|
-
schema: z.object({ message: z.string(), method: z.string() }),
|
|
25
|
-
}),
|
|
26
|
-
},
|
|
27
|
-
['/5']: {
|
|
28
|
-
method: HTTP.Methods.POST,
|
|
29
|
-
path: '/5',
|
|
30
|
-
authStrategies: [Authentication.STRATEGY_JWT], // Secure this endpoint
|
|
31
|
-
request: {
|
|
32
|
-
body: jsonContent({
|
|
33
|
-
description: 'Request body for POST',
|
|
34
|
-
schema: z.object({ name: z.string(), age: z.number().int().positive() }),
|
|
35
|
-
}),
|
|
36
|
-
},
|
|
37
|
-
responses: jsonResponse({
|
|
38
|
-
description: 'Test decorator POST endpoint',
|
|
39
|
-
schema: z.object({ id: z.string(), name: z.string(), age: z.number() }),
|
|
40
|
-
}),
|
|
41
|
-
},
|
|
42
|
-
} as const;
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Then, use the decorators in your controller class. The `TRouteContext` type provides a fully typed context, including request parameters, body, and response types.
|
|
46
|
-
|
|
47
|
-
**`src/controllers/test/controller.ts`**
|
|
48
|
-
```typescript
|
|
49
|
-
import {
|
|
50
|
-
BaseController,
|
|
51
|
-
controller,
|
|
52
|
-
get,
|
|
53
|
-
post,
|
|
54
|
-
TRouteContext,
|
|
55
|
-
HTTP,
|
|
56
|
-
} from '@venizia/ignis';
|
|
57
|
-
import { ROUTE_CONFIGS } from './definitions';
|
|
58
|
-
|
|
59
|
-
@controller({ path: '/test' })
|
|
60
|
-
export class TestController extends BaseController {
|
|
61
|
-
// ...
|
|
62
|
-
|
|
63
|
-
@get({ configs: ROUTE_CONFIGS['/4'] })
|
|
64
|
-
getWithDecorator(context: TRouteContext<(typeof ROUTE_CONFIGS)['/4']>) {
|
|
65
|
-
// context is fully typed!
|
|
66
|
-
return context.json({ message: 'Hello from decorator', method: 'GET' }, HTTP.ResultCodes.RS_2.Ok);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
@post({ configs: ROUTE_CONFIGS['/5'] })
|
|
70
|
-
createWithDecorator(context: TRouteContext<(typeof ROUTE_CONFIGS)['/5']>) {
|
|
71
|
-
// context.req.valid('json') is automatically typed as { name: string, age: number }
|
|
72
|
-
const body = context.req.valid('json');
|
|
73
|
-
|
|
74
|
-
// The response is validated against the schema
|
|
75
|
-
return context.json(
|
|
76
|
-
{
|
|
77
|
-
id: crypto.randomUUID(),
|
|
78
|
-
name: body.name,
|
|
79
|
-
age: body.age,
|
|
80
|
-
},
|
|
81
|
-
HTTP.ResultCodes.RS_2.Ok,
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Example 2: Manual Route Definition in `binding()`
|
|
88
|
-
|
|
89
|
-
You can also define routes manually within the controller's `binding()` method using `defineRoute` or `bindRoute`. This is useful for more complex scenarios or for developers who prefer a non-decorator syntax.
|
|
90
|
-
|
|
91
|
-
**`src/controllers/test/controller.ts`**
|
|
92
|
-
```typescript
|
|
93
|
-
import { BaseController, controller, HTTP, ValueOrPromise } from '@venizia/ignis';
|
|
94
|
-
import { ROUTE_CONFIGS } from './definitions';
|
|
95
|
-
|
|
96
|
-
@controller({ path: '/test' })
|
|
97
|
-
export class TestController extends BaseController {
|
|
98
|
-
// ...
|
|
99
|
-
override binding(): ValueOrPromise<void> {
|
|
100
|
-
// Using 'defineRoute'
|
|
101
|
-
this.defineRoute({
|
|
102
|
-
configs: ROUTE_CONFIGS['/1'],
|
|
103
|
-
handler: context => {
|
|
104
|
-
return context.json({ message: 'Hello' }, HTTP.ResultCodes.RS_2.Ok);
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Using 'bindRoute' for a fluent API
|
|
109
|
-
this.bindRoute({
|
|
110
|
-
configs: ROUTE_CONFIGS['/3'],
|
|
111
|
-
}).to({
|
|
112
|
-
handler: context => {
|
|
113
|
-
return context.json({ message: 'Hello 3' }, HTTP.ResultCodes.RS_2.Ok);
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
// ...
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Example 3: Auto-Generated CRUD Controller
|
|
122
|
-
|
|
123
|
-
For standard database entities, you can use `ControllerFactory.defineCrudController` to instantly generate a controller with a full set of CRUD endpoints.
|
|
124
|
-
|
|
125
|
-
**`src/controllers/configuration.controller.ts`**
|
|
126
|
-
```typescript
|
|
127
|
-
import { Configuration } from '@/models';
|
|
128
|
-
import { ConfigurationRepository } from '@/repositories';
|
|
129
|
-
import {
|
|
130
|
-
BindingKeys,
|
|
131
|
-
BindingNamespaces,
|
|
132
|
-
controller,
|
|
133
|
-
ControllerFactory,
|
|
134
|
-
inject,
|
|
135
|
-
} from '@venizia/ignis';
|
|
136
|
-
|
|
137
|
-
const BASE_PATH = '/configurations';
|
|
138
|
-
|
|
139
|
-
// 1. The factory generates a controller class with all CRUD routes
|
|
140
|
-
const _Controller = ControllerFactory.defineCrudController({
|
|
141
|
-
repository: { name: ConfigurationRepository.name },
|
|
142
|
-
controller: {
|
|
143
|
-
name: 'ConfigurationController',
|
|
144
|
-
basePath: BASE_PATH,
|
|
145
|
-
},
|
|
146
|
-
entity: () => Configuration, // The entity is used to generate OpenAPI schemas
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// 2. Extend the generated controller to inject the repository
|
|
150
|
-
@controller({ path: BASE_PATH })
|
|
151
|
-
export class ConfigurationController extends _Controller {
|
|
152
|
-
constructor(
|
|
153
|
-
@inject({
|
|
154
|
-
key: BindingKeys.build({
|
|
155
|
-
namespace: BindingNamespaces.REPOSITORY,
|
|
156
|
-
key: ConfigurationRepository.name,
|
|
157
|
-
}),
|
|
158
|
-
})
|
|
159
|
-
repository: ConfigurationRepository,
|
|
160
|
-
) {
|
|
161
|
-
super(repository);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
This automatically creates endpoints like `GET /configurations`, `POST /configurations`, `GET /configurations/:id`, etc.
|
|
166
|
-
|
|
167
|
-
## Repository (Data Access) Usage
|
|
168
|
-
|
|
169
|
-
Repositories are used to interact with your database. The `DefaultCRUDRepository` provides a rich set of methods for data manipulation. Here are examples from the `postConfigure` method in `src/application.ts`, which demonstrates how to use an injected repository.
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
// In src/application.ts
|
|
173
|
-
|
|
174
|
-
// Get the repository instance from the DI container
|
|
175
|
-
const configurationRepository = this.get<ConfigurationRepository>({
|
|
176
|
-
key: BindingKeys.build({
|
|
177
|
-
namespace: BindingNamespaces.REPOSITORY,
|
|
178
|
-
key: ConfigurationRepository.name,
|
|
179
|
-
}),
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// --- Find One Record ---
|
|
183
|
-
const record = await configurationRepository.findOne({
|
|
184
|
-
filter: { where: { code: 'CODE_1' } },
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
// --- Find Multiple Records with Relations ---
|
|
188
|
-
const records = await configurationRepository.find({
|
|
189
|
-
filter: {
|
|
190
|
-
where: { code: 'CODE_2' },
|
|
191
|
-
fields: { id: true, code: true, createdBy: true },
|
|
192
|
-
limit: 100,
|
|
193
|
-
include: [{ relation: 'creator' }], // Eager load the 'creator' relation
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// --- Create a Single Record ---
|
|
198
|
-
const newRecord = await configurationRepository.create({
|
|
199
|
-
data: {
|
|
200
|
-
code: 'NEW_CODE',
|
|
201
|
-
group: 'SYSTEM',
|
|
202
|
-
dataType: 'TEXT',
|
|
203
|
-
tValue: 'some value',
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// --- Create Multiple Records ---
|
|
208
|
-
const newRecords = await configurationRepository.createAll({
|
|
209
|
-
data: [
|
|
210
|
-
{ code: 'CODE_A', group: 'SYSTEM' },
|
|
211
|
-
{ code: 'CODE_B', group: 'SYSTEM' },
|
|
212
|
-
],
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// --- Update a Record by ID ---
|
|
216
|
-
const updated = await configurationRepository.updateById({
|
|
217
|
-
id: 'some-uuid',
|
|
218
|
-
data: { tValue: 'new value' },
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// --- Delete a Record by ID ---
|
|
222
|
-
const deleted = await configurationRepository.deleteById({
|
|
223
|
-
id: newRecord.data!.id,
|
|
224
|
-
options: { shouldReturn: true }, // Option to return the deleted record
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
## Server-Side Rendering (JSX)
|
|
228
|
-
|
|
229
|
-
Ignis supports server-side rendering using Hono's JSX middleware. This is useful for returning HTML content, such as landing pages or simple admin views.
|
|
230
|
-
|
|
231
|
-
**Usage:**
|
|
232
|
-
|
|
233
|
-
Use `defineJSXRoute` in your controller and `htmlResponse` for documentation.
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
import { BaseController, controller, htmlResponse } from '@venizia/ignis';
|
|
237
|
-
|
|
238
|
-
@controller({ path: '/pages' })
|
|
239
|
-
export class PageController extends BaseController {
|
|
240
|
-
|
|
241
|
-
override binding(): void {
|
|
242
|
-
this.defineJSXRoute({
|
|
243
|
-
configs: {
|
|
244
|
-
method: 'get',
|
|
245
|
-
path: '/welcome',
|
|
246
|
-
description: 'Welcome Page',
|
|
247
|
-
responses: htmlResponse({ description: 'HTML Welcome Page' }),
|
|
248
|
-
},
|
|
249
|
-
handler: (c) => {
|
|
250
|
-
const title = 'Welcome to Ignis';
|
|
251
|
-
|
|
252
|
-
// Return JSX directly
|
|
253
|
-
return c.html(
|
|
254
|
-
<html>
|
|
255
|
-
<head><title>{title}</title></head>
|
|
256
|
-
<body>
|
|
257
|
-
<h1>{title}</h1>
|
|
258
|
-
<p>Server-side rendered content.</p>
|
|
259
|
-
</body>
|
|
260
|
-
</html>
|
|
261
|
-
);
|
|
262
|
-
},
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
```
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
# Architectural Patterns
|
|
2
|
-
|
|
3
|
-
Ignis promotes separation of concerns, dependency injection, and modularity for scalable, maintainable applications.
|
|
4
|
-
|
|
5
|
-
> **Deep Dive:** See [Core Framework Reference](../../references/src-details/core.md) for implementation details.
|
|
6
|
-
|
|
7
|
-
## 1. Layered Architecture
|
|
8
|
-
|
|
9
|
-
Each layer has a single responsibility. Ignis supports **two architectural approaches**:
|
|
10
|
-
|
|
11
|
-
```mermaid
|
|
12
|
-
graph TD
|
|
13
|
-
Client[Client/API Consumer]
|
|
14
|
-
|
|
15
|
-
Client -->|HTTP Request| Controller[Controllers]
|
|
16
|
-
|
|
17
|
-
Controller -->|Simple CRUD| Repo[Repositories]
|
|
18
|
-
Controller -->|Complex Logic| Service[Services]
|
|
19
|
-
|
|
20
|
-
Service --> Repo
|
|
21
|
-
|
|
22
|
-
Repo --> DataSource[DataSources]
|
|
23
|
-
DataSource --> DB[(Database)]
|
|
24
|
-
|
|
25
|
-
style Service fill:#e1f5ff
|
|
26
|
-
style Repo fill:#fff4e1
|
|
27
|
-
style Controller fill:#ffe1f5
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
| Layer | Responsibility | Example |
|
|
31
|
-
|-------|---------------|---------|
|
|
32
|
-
| **Controllers** | Handle HTTP - parse requests, validate, format responses | `ConfigurationController` (uses `ControllerFactory`) |
|
|
33
|
-
| **Services** | Business logic - orchestrate operations | `AuthenticationService` (auth logic) |
|
|
34
|
-
| **Repositories** | Data access - CRUD operations | `ConfigurationRepository` (extends `DefaultCRUDRepository`) |
|
|
35
|
-
| **DataSources** | Database connections | `PostgresDataSource` (connects to PostgreSQL) |
|
|
36
|
-
| **Models** | Data structure - Drizzle schemas + Entity classes | `Configuration`, `User` models |
|
|
37
|
-
|
|
38
|
-
**Key Principle - Two Approaches:**
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
Simple CRUD (no business logic):
|
|
42
|
-
┌────────────┐
|
|
43
|
-
│ Controller │──────────────┐
|
|
44
|
-
└────────────┘ │
|
|
45
|
-
▼
|
|
46
|
-
┌──────────────┐
|
|
47
|
-
│ Repository │
|
|
48
|
-
└──────────────┘
|
|
49
|
-
│
|
|
50
|
-
▼
|
|
51
|
-
Database
|
|
52
|
-
|
|
53
|
-
Complex Logic (validation, orchestration):
|
|
54
|
-
┌────────────┐
|
|
55
|
-
│ Controller │────┐
|
|
56
|
-
└────────────┘ │
|
|
57
|
-
▼
|
|
58
|
-
┌─────────┐
|
|
59
|
-
│ Service │
|
|
60
|
-
└─────────┘
|
|
61
|
-
│
|
|
62
|
-
▼
|
|
63
|
-
┌──────────────┐
|
|
64
|
-
│ Repository │
|
|
65
|
-
└──────────────┘
|
|
66
|
-
│
|
|
67
|
-
▼
|
|
68
|
-
Database
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**When to use each:**
|
|
72
|
-
- **Controller → Repository** - Simple CRUD (list, get by ID, create, update, delete)
|
|
73
|
-
- **Controller → Service → Repository** - Business logic, validation, orchestrating multiple repositories
|
|
74
|
-
|
|
75
|
-
## 2. Dependency Injection (DI)
|
|
76
|
-
|
|
77
|
-
Classes declare dependencies in their constructor - the framework automatically provides them at runtime.
|
|
78
|
-
|
|
79
|
-
**Benefits:**
|
|
80
|
-
- Loosely coupled code
|
|
81
|
-
- Easy to test (mock dependencies)
|
|
82
|
-
- Easy to swap implementations
|
|
83
|
-
|
|
84
|
-
**Example:**
|
|
85
|
-
```typescript
|
|
86
|
-
@controller({ path: BASE_PATH })
|
|
87
|
-
export class ConfigurationController extends _Controller {
|
|
88
|
-
constructor(
|
|
89
|
-
// The @inject decorator tells the container to provide
|
|
90
|
-
// an instance of ConfigurationRepository here.
|
|
91
|
-
@inject({
|
|
92
|
-
key: BindingKeys.build({
|
|
93
|
-
namespace: BindingNamespaces.REPOSITORY,
|
|
94
|
-
key: ConfigurationRepository.name,
|
|
95
|
-
}),
|
|
96
|
-
})
|
|
97
|
-
repository: ConfigurationRepository,
|
|
98
|
-
) {
|
|
99
|
-
super(repository);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## 3. Component-Based Modularity
|
|
105
|
-
|
|
106
|
-
Components bundle a group of related, reusable, and pluggable features into self-contained modules. A single component can encapsulate multiple providers, services, controllers, and repositories, essentially functioning as a mini-application that can be easily "plugged in" to any Ignis project.
|
|
107
|
-
|
|
108
|
-
**Built-in Components:**
|
|
109
|
-
- `AuthenticateComponent` - JWT authentication
|
|
110
|
-
- `SwaggerComponent` - OpenAPI documentation
|
|
111
|
-
- `HealthCheckComponent` - Health check endpoint
|
|
112
|
-
- `RequestTrackerComponent` - Request logging
|
|
113
|
-
|
|
114
|
-
**Example:**
|
|
115
|
-
```typescript
|
|
116
|
-
// src/application.ts
|
|
117
|
-
|
|
118
|
-
export class Application extends BaseApplication {
|
|
119
|
-
// ...
|
|
120
|
-
preConfigure(): ValueOrPromise<void> {
|
|
121
|
-
// ...
|
|
122
|
-
// Registering components plugs their functionality into the application.
|
|
123
|
-
this.component(HealthCheckComponent);
|
|
124
|
-
this.component(SwaggerComponent);
|
|
125
|
-
// ...
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
This architecture keeps the main `Application` class clean and focused on high-level assembly, while the details of each feature are neatly encapsulated within their respective components.
|
|
130
|
-
|
|
131
|
-
## 4. Custom Components
|
|
132
|
-
|
|
133
|
-
You can encapsulate your own logic or third-party integrations (like Socket.IO, Redis, specific Cron jobs) into reusable Components.
|
|
134
|
-
|
|
135
|
-
**Structure of a Component:**
|
|
136
|
-
1. Extend `BaseComponent`.
|
|
137
|
-
2. Define default `bindings` (optional configuration/options).
|
|
138
|
-
3. Implement `binding()` to register services, providers, or attach logic to the application.
|
|
139
|
-
|
|
140
|
-
**Example (`SocketIOComponent`):**
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
import { BaseComponent, inject, CoreBindings, Binding } from '@venizia/ignis';
|
|
144
|
-
|
|
145
|
-
export class MySocketComponent extends BaseComponent {
|
|
146
|
-
constructor(
|
|
147
|
-
@inject({ key: CoreBindings.APPLICATION_INSTANCE }) private application: BaseApplication,
|
|
148
|
-
) {
|
|
149
|
-
super({
|
|
150
|
-
scope: MySocketComponent.name,
|
|
151
|
-
// Automatically register bindings when component is loaded
|
|
152
|
-
initDefault: { enable: true, container: application },
|
|
153
|
-
bindings: {
|
|
154
|
-
// Define default configuration binding
|
|
155
|
-
'my.socket.options': Binding.bind({ key: 'my.socket.options' }).toValue({ port: 8080 }),
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// The binding method is called during application startup (preConfigure)
|
|
161
|
-
override binding(): void {
|
|
162
|
-
const options = this.application.get({ key: 'my.socket.options' });
|
|
163
|
-
|
|
164
|
-
this.logger.info('Initializing Socket.IO with options: %j', options);
|
|
165
|
-
|
|
166
|
-
// Perform setup logic, register other services, etc.
|
|
167
|
-
// this.application.bind(...).toValue(...);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
```
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# Data Modeling
|
|
2
|
-
|
|
3
|
-
Ignis streamlines data modeling with Drizzle ORM by providing powerful helpers and "enrichers" that reduce boilerplate code for common schema patterns.
|
|
4
|
-
|
|
5
|
-
## 1. Base Entity
|
|
6
|
-
|
|
7
|
-
All entity models should extend `BaseEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
|
|
8
|
-
|
|
9
|
-
The recommended pattern is to define the schema and relations as **static properties** on the class. This keeps the definition self-contained and enables powerful type inference.
|
|
10
|
-
|
|
11
|
-
**Example (`src/models/entities/user.model.ts`):**
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import { BaseEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
|
|
15
|
-
import { pgTable } from 'drizzle-orm/pg-core';
|
|
16
|
-
|
|
17
|
-
@model({ type: 'entity' })
|
|
18
|
-
export class User extends BaseEntity<typeof User.schema> {
|
|
19
|
-
// 1. Define schema as a static property
|
|
20
|
-
static override schema = pgTable('User', {
|
|
21
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
22
|
-
...extraUserColumns({ idType: 'string' }),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// 2. Define relations as a static method (return empty array if none)
|
|
26
|
-
static override relations = () => [];
|
|
27
|
-
}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## 2. Schema Enrichers
|
|
31
|
-
|
|
32
|
-
Instead of manually defining common columns like primary keys, timestamps, or audit fields in every table, use Ignis "enrichers".
|
|
33
|
-
|
|
34
|
-
**Available Enrichers:**
|
|
35
|
-
|
|
36
|
-
| Enricher | Description | Columns Added |
|
|
37
|
-
|----------|-------------|---------------|
|
|
38
|
-
| `generateIdColumnDefs` | Adds a Primary Key | `id` (string/UUID or number/Serial) |
|
|
39
|
-
| `generateTzColumnDefs` | Adds timestamps | `createdAt`, `modifiedAt` (auto-updating) |
|
|
40
|
-
| `generateUserAuditColumnDefs` | Adds audit fields | `createdBy`, `modifiedBy` |
|
|
41
|
-
| `generateDataTypeColumnDefs` | Adds generic value fields | `nValue` (number), `tValue` (text), `jValue` (json), etc. |
|
|
42
|
-
| `extraUserColumns` | Comprehensive user fields | Combines audit, timestamps, status, and type fields |
|
|
43
|
-
|
|
44
|
-
**Usage Example:**
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import {
|
|
48
|
-
generateIdColumnDefs,
|
|
49
|
-
generateTzColumnDefs,
|
|
50
|
-
generateUserAuditColumnDefs,
|
|
51
|
-
} from '@venizia/ignis';
|
|
52
|
-
import { pgTable, text } from 'drizzle-orm/pg-core';
|
|
53
|
-
|
|
54
|
-
export const configurationTable = pgTable(
|
|
55
|
-
'Configuration',
|
|
56
|
-
{
|
|
57
|
-
// 1. Auto-generate UUID Primary Key
|
|
58
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
59
|
-
|
|
60
|
-
// 2. Auto-generate createdAt / modifiedAt
|
|
61
|
-
...generateTzColumnDefs(),
|
|
62
|
-
|
|
63
|
-
// 3. Auto-generate createdBy / modifiedBy
|
|
64
|
-
...generateUserAuditColumnDefs({
|
|
65
|
-
created: { dataType: 'string', columnName: 'created_by' },
|
|
66
|
-
modified: { dataType: 'string', columnName: 'modified_by' },
|
|
67
|
-
}),
|
|
68
|
-
|
|
69
|
-
// 4. Your custom columns
|
|
70
|
-
code: text('code').notNull(),
|
|
71
|
-
description: text('description'),
|
|
72
|
-
group: text('group').notNull(),
|
|
73
|
-
},
|
|
74
|
-
(table) => [
|
|
75
|
-
// Define indexes/constraints here
|
|
76
|
-
unique('UQ_code').on(table.code),
|
|
77
|
-
]
|
|
78
|
-
);
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## 3. Defining Relations
|
|
82
|
-
|
|
83
|
-
Relations are defined using the `TRelationConfig` structure within the static `relations` method of your model.
|
|
84
|
-
|
|
85
|
-
**Example (`src/models/entities/configuration.model.ts`):**
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
import {
|
|
89
|
-
BaseEntity,
|
|
90
|
-
model,
|
|
91
|
-
RelationTypes,
|
|
92
|
-
TRelationConfig,
|
|
93
|
-
} from '@venizia/ignis';
|
|
94
|
-
import { User } from './user.model';
|
|
95
|
-
|
|
96
|
-
@model({ type: 'entity' })
|
|
97
|
-
export class Configuration extends BaseEntity<typeof Configuration.schema> {
|
|
98
|
-
// ... schema definition ...
|
|
99
|
-
|
|
100
|
-
// Define relations
|
|
101
|
-
static override relations = (): TRelationConfig[] => [
|
|
102
|
-
{
|
|
103
|
-
name: 'creator',
|
|
104
|
-
type: RelationTypes.ONE,
|
|
105
|
-
schema: User.schema,
|
|
106
|
-
metadata: {
|
|
107
|
-
fields: [Configuration.schema.createdBy],
|
|
108
|
-
references: [User.schema.id],
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
];
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## 4. Repositories and Auto-Discovery
|
|
116
|
-
|
|
117
|
-
Ignis simplifies the connection between models, repositories, and datasources.
|
|
118
|
-
|
|
119
|
-
### DataSource Auto-Discovery
|
|
120
|
-
|
|
121
|
-
DataSources automatically discover their schema from the repositories that bind to them. You **do not** need to manually register schemas in the DataSource constructor.
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// src/datasources/postgres.datasource.ts
|
|
125
|
-
@datasource({ driver: 'node-postgres' })
|
|
126
|
-
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
127
|
-
constructor() {
|
|
128
|
-
super({
|
|
129
|
-
name: PostgresDataSource.name,
|
|
130
|
-
config: { /* connection config */ },
|
|
131
|
-
// NO schema property needed - auto-discovered!
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
override configure(): ValueOrPromise<void> {
|
|
136
|
-
// This method automatically collects all schemas from bound repositories
|
|
137
|
-
const schema = this.getSchema();
|
|
138
|
-
this.connector = drizzle({ client: new Pool(this.settings), schema });
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Repository Binding
|
|
144
|
-
|
|
145
|
-
Repositories use the `@repository` decorator to bind a **Model** to a **DataSource**. This binding is what powers the auto-discovery mechanism.
|
|
146
|
-
|
|
147
|
-
**Pattern 1: Zero Boilerplate (Recommended)**
|
|
148
|
-
|
|
149
|
-
For most repositories, you don't need a constructor. The DataSource is automatically injected.
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
@repository({ model: Configuration, dataSource: PostgresDataSource })
|
|
153
|
-
export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {
|
|
154
|
-
// No constructor needed!
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
**Pattern 2: Explicit Injection (Advanced)**
|
|
159
|
-
|
|
160
|
-
If you need to perform custom initialization or inject additional dependencies, you can define a constructor. **Important:** The first parameter must be the DataSource.
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
@repository({ model: User, dataSource: PostgresDataSource })
|
|
164
|
-
export class UserRepository extends ReadableRepository<typeof User.schema> {
|
|
165
|
-
constructor(
|
|
166
|
-
@inject({ key: 'datasources.PostgresDataSource' })
|
|
167
|
-
dataSource: PostgresDataSource,
|
|
168
|
-
) {
|
|
169
|
-
super(dataSource);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Custom methods
|
|
173
|
-
async findByRealm(realm: string) {
|
|
174
|
-
return this.findOne({ filter: { where: { realm } } });
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
```
|