@venizia/ignis-docs 0.0.2 → 0.0.4-0
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 +647 -182
- 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 +86 -0
- package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
- 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 +8 -1
- package/wiki/changelogs/planned-schema-migrator.md +2 -10
- 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/guides/core-concepts/components.md +122 -0
- 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 +30 -26
- package/wiki/references/base/components.md +532 -31
- package/wiki/references/base/controllers.md +136 -38
- package/wiki/references/base/datasources.md +108 -5
- 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 +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -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 -549
- 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 +15 -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 +167 -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/changelogs/planned-transaction-support.md +0 -216
- 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 -88
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/components.md +0 -98
- package/wiki/get-started/core-concepts/persistent.md +0 -543
- 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,543 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Persistent Layer
|
|
3
|
-
description: Models, DataSources, and Repositories in Ignis
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Persistent Layer: Models, DataSources, and Repositories
|
|
7
|
-
|
|
8
|
-
The persistent layer manages data using [Drizzle ORM](https://orm.drizzle.team/) for type-safe database access and the Repository pattern for data abstraction.
|
|
9
|
-
|
|
10
|
-
**Three main components:**
|
|
11
|
-
|
|
12
|
-
- **Models** - Define data structure (static schema + relations on Entity class)
|
|
13
|
-
- **DataSources** - Manage database connections with auto-discovery
|
|
14
|
-
- **Repositories** - Provide CRUD operations with zero boilerplate
|
|
15
|
-
|
|
16
|
-
## 1. Models: Defining Your Data Structure
|
|
17
|
-
|
|
18
|
-
A model in Ignis is a single class with static properties for schema and relations. No separate variables needed.
|
|
19
|
-
|
|
20
|
-
### Creating a Basic Model
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
// src/models/entities/user.model.ts
|
|
24
|
-
import { BaseEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
|
|
25
|
-
import { pgTable } from 'drizzle-orm/pg-core';
|
|
26
|
-
|
|
27
|
-
@model({ type: 'entity' })
|
|
28
|
-
export class User extends BaseEntity<typeof User.schema> {
|
|
29
|
-
// Define schema as static property
|
|
30
|
-
static override schema = pgTable('User', {
|
|
31
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
32
|
-
...extraUserColumns({ idType: 'string' }),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Relations (empty array if none)
|
|
36
|
-
static override relations = () => [];
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**Key points:**
|
|
41
|
-
|
|
42
|
-
- Schema is defined inline as `static override schema`
|
|
43
|
-
- Relations are defined as `static override relations`
|
|
44
|
-
- No constructor needed - BaseEntity auto-discovers from static properties
|
|
45
|
-
- Type parameter uses `typeof User.schema` (self-referencing)
|
|
46
|
-
|
|
47
|
-
### Creating a Model with Relations
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
// src/models/entities/configuration.model.ts
|
|
51
|
-
import {
|
|
52
|
-
BaseEntity,
|
|
53
|
-
generateDataTypeColumnDefs,
|
|
54
|
-
generateIdColumnDefs,
|
|
55
|
-
generateTzColumnDefs,
|
|
56
|
-
generateUserAuditColumnDefs,
|
|
57
|
-
model,
|
|
58
|
-
RelationTypes,
|
|
59
|
-
TRelationConfig,
|
|
60
|
-
} from '@venizia/ignis';
|
|
61
|
-
import { foreignKey, index, pgTable, text, unique } from 'drizzle-orm/pg-core';
|
|
62
|
-
import { User } from './user.model';
|
|
63
|
-
|
|
64
|
-
@model({ type: 'entity' })
|
|
65
|
-
export class Configuration extends BaseEntity<typeof Configuration.schema> {
|
|
66
|
-
static override schema = pgTable(
|
|
67
|
-
'Configuration',
|
|
68
|
-
{
|
|
69
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
70
|
-
...generateTzColumnDefs(),
|
|
71
|
-
...generateDataTypeColumnDefs(),
|
|
72
|
-
...generateUserAuditColumnDefs({
|
|
73
|
-
created: { dataType: 'string', columnName: 'created_by' },
|
|
74
|
-
modified: { dataType: 'string', columnName: 'modified_by' },
|
|
75
|
-
}),
|
|
76
|
-
code: text('code').notNull(),
|
|
77
|
-
description: text('description'),
|
|
78
|
-
group: text('group').notNull(),
|
|
79
|
-
},
|
|
80
|
-
def => [
|
|
81
|
-
unique('UQ_Configuration_code').on(def.code),
|
|
82
|
-
index('IDX_Configuration_group').on(def.group),
|
|
83
|
-
foreignKey({
|
|
84
|
-
columns: [def.createdBy],
|
|
85
|
-
foreignColumns: [User.schema.id], // Reference User.schema, not a separate variable
|
|
86
|
-
name: 'FK_Configuration_createdBy_User_id',
|
|
87
|
-
}),
|
|
88
|
-
],
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
// Define relations using TRelationConfig array
|
|
92
|
-
static override relations = (): TRelationConfig[] => [
|
|
93
|
-
{
|
|
94
|
-
name: 'creator',
|
|
95
|
-
type: RelationTypes.ONE,
|
|
96
|
-
schema: User.schema,
|
|
97
|
-
metadata: {
|
|
98
|
-
fields: [Configuration.schema.createdBy],
|
|
99
|
-
references: [User.schema.id],
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: 'modifier',
|
|
104
|
-
type: RelationTypes.ONE,
|
|
105
|
-
schema: User.schema,
|
|
106
|
-
metadata: {
|
|
107
|
-
fields: [Configuration.schema.modifiedBy],
|
|
108
|
-
references: [User.schema.id],
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
];
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**Key points:**
|
|
116
|
-
|
|
117
|
-
- Relations use `TRelationConfig[]` format directly
|
|
118
|
-
- Reference other models via `Model.schema` (e.g., `User.schema.id`)
|
|
119
|
-
- Relation names (`creator`, `modifier`) are used in queries with `include`
|
|
120
|
-
|
|
121
|
-
### Understanding Enrichers
|
|
122
|
-
|
|
123
|
-
Enrichers are helper functions that generate common database columns automatically.
|
|
124
|
-
|
|
125
|
-
**Without enrichers:**
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
static override schema = pgTable('User', {
|
|
129
|
-
id: uuid('id').defaultRandom().primaryKey(),
|
|
130
|
-
status: text('status').notNull().default('ACTIVE'),
|
|
131
|
-
createdBy: text('created_by'),
|
|
132
|
-
modifiedBy: text('modified_by'),
|
|
133
|
-
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
134
|
-
modifiedAt: timestamp('modified_at', { withTimezone: true }).notNull().defaultNow(),
|
|
135
|
-
// ... your fields
|
|
136
|
-
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
**With enrichers:**
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
static override schema = pgTable('User', {
|
|
143
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }), // id (UUID)
|
|
144
|
-
...extraUserColumns({ idType: 'string' }), // status, audit fields, timestamps
|
|
145
|
-
// ... your fields
|
|
146
|
-
});
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### Available Enrichers
|
|
150
|
-
|
|
151
|
-
| Enricher | Columns Added | Use Case |
|
|
152
|
-
|----------|---------------|----------|
|
|
153
|
-
| `generateIdColumnDefs()` | `id` (UUID or number) | Every table |
|
|
154
|
-
| `generateTzColumnDefs()` | `createdAt`, `modifiedAt` | Track timestamps |
|
|
155
|
-
| `generateUserAuditColumnDefs()` | `createdBy`, `modifiedBy` | Track who created/updated |
|
|
156
|
-
| `generateDataTypeColumnDefs()` | `dataType`, `tValue`, `nValue`, etc. | Configuration tables |
|
|
157
|
-
| `extraUserColumns()` | Combines audit + status + type | Full-featured entities |
|
|
158
|
-
|
|
159
|
-
:::tip
|
|
160
|
-
For a complete list of enrichers and options, see the [Schema Enrichers Reference](../../references/base/models.md#schema-enrichers).
|
|
161
|
-
:::
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
## 2. DataSources: Connecting to Your Database
|
|
166
|
-
|
|
167
|
-
A DataSource manages database connections and supports **schema auto-discovery** from repositories.
|
|
168
|
-
|
|
169
|
-
### Creating a DataSource
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
// src/datasources/postgres.datasource.ts
|
|
173
|
-
import {
|
|
174
|
-
BaseDataSource,
|
|
175
|
-
datasource,
|
|
176
|
-
TNodePostgresConnector,
|
|
177
|
-
ValueOrPromise,
|
|
178
|
-
} from '@venizia/ignis';
|
|
179
|
-
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
180
|
-
import { Pool } from 'pg';
|
|
181
|
-
|
|
182
|
-
interface IDSConfigs {
|
|
183
|
-
host: string;
|
|
184
|
-
port: number;
|
|
185
|
-
database: string;
|
|
186
|
-
user: string;
|
|
187
|
-
password: string;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
@datasource({ driver: 'node-postgres' })
|
|
191
|
-
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
192
|
-
constructor() {
|
|
193
|
-
super({
|
|
194
|
-
name: PostgresDataSource.name,
|
|
195
|
-
config: {
|
|
196
|
-
host: process.env.POSTGRES_HOST ?? 'localhost',
|
|
197
|
-
port: +(process.env.POSTGRES_PORT ?? 5432),
|
|
198
|
-
database: process.env.POSTGRES_DATABASE ?? 'mydb',
|
|
199
|
-
user: process.env.POSTGRES_USER ?? 'postgres',
|
|
200
|
-
password: process.env.POSTGRES_PASSWORD ?? '',
|
|
201
|
-
},
|
|
202
|
-
// No schema needed - auto-discovered from @repository bindings!
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
override configure(): ValueOrPromise<void> {
|
|
207
|
-
// getSchema() auto-discovers models from @repository bindings
|
|
208
|
-
const schema = this.getSchema();
|
|
209
|
-
|
|
210
|
-
this.logger.debug(
|
|
211
|
-
'[configure] Auto-discovered schema | Keys: %o',
|
|
212
|
-
Object.keys(schema),
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
const client = new Pool(this.settings);
|
|
216
|
-
this.connector = drizzle({ client, schema });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
override getConnectionString(): ValueOrPromise<string> {
|
|
220
|
-
const { host, port, user, password, database } = this.settings;
|
|
221
|
-
return `postgresql://${user}:${password}@${host}:${port}/${database}`;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
**How auto-discovery works:**
|
|
227
|
-
|
|
228
|
-
1. `@repository` decorators register model-datasource bindings
|
|
229
|
-
2. When `configure()` is called, `getSchema()` collects all bound models
|
|
230
|
-
3. Drizzle is initialized with the complete schema
|
|
231
|
-
|
|
232
|
-
### Manual Schema (Optional)
|
|
233
|
-
|
|
234
|
-
If you need explicit control, you can still provide schema manually:
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
@datasource({ driver: 'node-postgres' })
|
|
238
|
-
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
239
|
-
constructor() {
|
|
240
|
-
super({
|
|
241
|
-
name: PostgresDataSource.name,
|
|
242
|
-
config: { /* ... */ },
|
|
243
|
-
schema: {
|
|
244
|
-
User: User.schema,
|
|
245
|
-
Configuration: Configuration.schema,
|
|
246
|
-
// Add relations if using Drizzle's relational queries
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Registering a DataSource
|
|
254
|
-
|
|
255
|
-
```typescript
|
|
256
|
-
// src/application.ts
|
|
257
|
-
export class Application extends BaseApplication {
|
|
258
|
-
preConfigure(): ValueOrPromise<void> {
|
|
259
|
-
this.dataSource(PostgresDataSource);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
## 3. Repositories: The Data Access Layer
|
|
267
|
-
|
|
268
|
-
Repositories provide type-safe CRUD operations. Use `@repository` decorator with both `model` and `dataSource` for auto-discovery.
|
|
269
|
-
|
|
270
|
-
### Pattern 1: Zero Boilerplate (Recommended)
|
|
271
|
-
|
|
272
|
-
The simplest approach - everything is auto-resolved:
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
// src/repositories/configuration.repository.ts
|
|
276
|
-
import { Configuration } from '@/models/entities';
|
|
277
|
-
import { PostgresDataSource } from '@/datasources/postgres.datasource';
|
|
278
|
-
import { DefaultCRUDRepository, repository } from '@venizia/ignis';
|
|
279
|
-
|
|
280
|
-
@repository({
|
|
281
|
-
model: Configuration,
|
|
282
|
-
dataSource: PostgresDataSource,
|
|
283
|
-
})
|
|
284
|
-
export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {
|
|
285
|
-
// No constructor needed!
|
|
286
|
-
|
|
287
|
-
async findByCode(code: string) {
|
|
288
|
-
return this.findOne({ filter: { where: { code } } });
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
async findByGroup(group: string) {
|
|
292
|
-
return this.find({ filter: { where: { group } } });
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### Pattern 2: Explicit @inject
|
|
298
|
-
|
|
299
|
-
When you need constructor control (e.g., read-only repository or additional dependencies):
|
|
300
|
-
|
|
301
|
-
```typescript
|
|
302
|
-
// src/repositories/user.repository.ts
|
|
303
|
-
import { User } from '@/models/entities';
|
|
304
|
-
import { PostgresDataSource } from '@/datasources/postgres.datasource';
|
|
305
|
-
import { inject, ReadableRepository, repository } from '@venizia/ignis';
|
|
306
|
-
import { CacheService } from '@/services/cache.service';
|
|
307
|
-
|
|
308
|
-
@repository({ model: User, dataSource: PostgresDataSource })
|
|
309
|
-
export class UserRepository extends ReadableRepository<typeof User.schema> {
|
|
310
|
-
constructor(
|
|
311
|
-
// First parameter MUST be DataSource injection
|
|
312
|
-
@inject({ key: 'datasources.PostgresDataSource' })
|
|
313
|
-
dataSource: PostgresDataSource, // Must be concrete type, not 'any'
|
|
314
|
-
|
|
315
|
-
// After first arg, you can inject any additional dependencies
|
|
316
|
-
@inject({ key: 'some.cache' })
|
|
317
|
-
private cache: SomeCache,
|
|
318
|
-
) {
|
|
319
|
-
super(dataSource);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async findByRealm(realm: string) {
|
|
323
|
-
// Use injected dependencies
|
|
324
|
-
const cached = await this.cacheService.get(`user:realm:${realm}`);
|
|
325
|
-
if (cached) {
|
|
326
|
-
return cached;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return this.findOne({ filter: { where: { realm } } });
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
> **Important:**
|
|
335
|
-
> - First constructor parameter **MUST** be the DataSource injection
|
|
336
|
-
> - After the first argument, you can inject any additional dependencies you need
|
|
337
|
-
> - When `@inject` is at param index 0, auto-injection is skipped
|
|
338
|
-
|
|
339
|
-
### Repository Types
|
|
340
|
-
|
|
341
|
-
| Type | Description |
|
|
342
|
-
|------|-------------|
|
|
343
|
-
| `DefaultCRUDRepository` | Full read/write operations |
|
|
344
|
-
| `ReadableRepository` | Read-only operations |
|
|
345
|
-
| `PersistableRepository` | Write operations only |
|
|
346
|
-
|
|
347
|
-
### Querying Data
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
const repo = this.get<ConfigurationRepository>({ key: 'repositories.ConfigurationRepository' });
|
|
351
|
-
|
|
352
|
-
// Find multiple records
|
|
353
|
-
const configs = await repo.find({
|
|
354
|
-
filter: {
|
|
355
|
-
where: { group: 'SYSTEM' },
|
|
356
|
-
limit: 10,
|
|
357
|
-
order: ['createdAt DESC'],
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// Find one record
|
|
362
|
-
const config = await repo.findOne({
|
|
363
|
-
filter: { where: { code: 'APP_NAME' } }
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// Create a record
|
|
367
|
-
const newConfig = await repo.create({
|
|
368
|
-
data: {
|
|
369
|
-
code: 'NEW_SETTING',
|
|
370
|
-
group: 'SYSTEM',
|
|
371
|
-
description: 'A new setting',
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
// Update by ID
|
|
376
|
-
await repo.updateById({
|
|
377
|
-
id: 'uuid-here',
|
|
378
|
-
data: { description: 'Updated description' }
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
// Delete by ID
|
|
382
|
-
await repo.deleteById({ id: 'uuid-here' });
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
### Querying with Relations
|
|
386
|
-
|
|
387
|
-
Use `include` to fetch related data. The relation name must match what you defined in `static relations`:
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
const configWithCreator = await repo.findOne({
|
|
391
|
-
filter: {
|
|
392
|
-
where: { code: 'APP_NAME' },
|
|
393
|
-
include: [{ relation: 'creator' }],
|
|
394
|
-
},
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
console.log('Created by:', configWithCreator.creator.name);
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### Registering Repositories
|
|
401
|
-
|
|
402
|
-
```typescript
|
|
403
|
-
// src/application.ts
|
|
404
|
-
export class Application extends BaseApplication {
|
|
405
|
-
preConfigure(): ValueOrPromise<void> {
|
|
406
|
-
this.dataSource(PostgresDataSource);
|
|
407
|
-
this.repository(UserRepository);
|
|
408
|
-
this.repository(ConfigurationRepository);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
## 4. Advanced Topics
|
|
416
|
-
|
|
417
|
-
### Performance: Core API Optimization
|
|
418
|
-
|
|
419
|
-
Ignis automatically optimizes "flat" queries (no relations, no field selection) by using Drizzle's Core API. This provides **~15-20% faster** queries for simple reads.
|
|
420
|
-
|
|
421
|
-
### Transactions (Current)
|
|
422
|
-
|
|
423
|
-
Currently, use Drizzle's callback-based `connector.transaction` for atomic operations:
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
const ds = this.get<PostgresDataSource>({ key: 'datasources.PostgresDataSource' });
|
|
427
|
-
|
|
428
|
-
await ds.connector.transaction(async (tx) => {
|
|
429
|
-
await tx.insert(User.schema).values({ /* ... */ });
|
|
430
|
-
await tx.insert(Configuration.schema).values({ /* ... */ });
|
|
431
|
-
});
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
> **Note:** This callback-based approach requires all transaction logic to be in one callback. See [Section 5](#5-transactions-planned) for the planned improvement.
|
|
435
|
-
|
|
436
|
-
### Modular Persistence with Components
|
|
437
|
-
|
|
438
|
-
Bundle related persistence resources into Components for better organization:
|
|
439
|
-
|
|
440
|
-
```typescript
|
|
441
|
-
export class UserManagementComponent extends BaseComponent {
|
|
442
|
-
override binding() {
|
|
443
|
-
this.application.dataSource(PostgresDataSource);
|
|
444
|
-
this.application.repository(UserRepository);
|
|
445
|
-
this.application.repository(ProfileRepository);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
|
|
452
|
-
## 5. Transactions (Planned)
|
|
453
|
-
|
|
454
|
-
> **Status:** Planned - Not yet implemented. See [full plan](../../changelogs/planned-transaction-support).
|
|
455
|
-
|
|
456
|
-
### The Problem
|
|
457
|
-
|
|
458
|
-
Drizzle's callback-based transactions make it hard to pass transactions across services:
|
|
459
|
-
|
|
460
|
-
```typescript
|
|
461
|
-
// Current: Everything must be inside the callback
|
|
462
|
-
await ds.connector.transaction(async (tx) => {
|
|
463
|
-
// Can't easily call other services with this tx
|
|
464
|
-
});
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
### Planned Solution
|
|
468
|
-
|
|
469
|
-
Loopback 4-style explicit transaction objects that can be passed anywhere:
|
|
470
|
-
|
|
471
|
-
```typescript
|
|
472
|
-
// Start transaction from repository
|
|
473
|
-
const tx = await userRepo.beginTransaction({
|
|
474
|
-
isolationLevel: 'SERIALIZABLE' // Optional, defaults to 'READ COMMITTED'
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
// Pass tx to multiple services/repositories
|
|
479
|
-
const user = await userRepo.create({ data, options: { transaction: tx } });
|
|
480
|
-
await profileRepo.create({ data: { userId: user.id }, options: { transaction: tx } });
|
|
481
|
-
await orderService.createInitialOrder(user.id, { transaction: tx });
|
|
482
|
-
|
|
483
|
-
await tx.commit();
|
|
484
|
-
} catch (err) {
|
|
485
|
-
await tx.rollback();
|
|
486
|
-
throw err;
|
|
487
|
-
}
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
### Isolation Levels
|
|
491
|
-
|
|
492
|
-
| Level | Description | Use Case |
|
|
493
|
-
|-------|-------------|----------|
|
|
494
|
-
| `READ COMMITTED` | Default. Sees only committed data | General use |
|
|
495
|
-
| `REPEATABLE READ` | Snapshot from transaction start | Reports, consistent reads |
|
|
496
|
-
| `SERIALIZABLE` | Full isolation, may throw errors | Financial, critical data |
|
|
497
|
-
|
|
498
|
-
### Benefits
|
|
499
|
-
|
|
500
|
-
| Aspect | Current (Callback) | Planned (Pass-through) |
|
|
501
|
-
|--------|-------------------|------------------------|
|
|
502
|
-
| Service composition | Hard | Easy - pass tx anywhere |
|
|
503
|
-
| Separation of concerns | Services coupled | Services independent |
|
|
504
|
-
| Testing | Complex mocking | Easy to mock tx |
|
|
505
|
-
| Code organization | Nested callbacks | Flat, sequential |
|
|
506
|
-
|
|
507
|
-
---
|
|
508
|
-
|
|
509
|
-
## Quick Reference
|
|
510
|
-
|
|
511
|
-
### Model Template
|
|
512
|
-
|
|
513
|
-
```typescript
|
|
514
|
-
import { BaseEntity, generateIdColumnDefs, model, TRelationConfig } from '@venizia/ignis';
|
|
515
|
-
import { pgTable, text } from 'drizzle-orm/pg-core';
|
|
516
|
-
|
|
517
|
-
@model({ type: 'entity' })
|
|
518
|
-
export class MyModel extends BaseEntity<typeof MyModel.schema> {
|
|
519
|
-
static override schema = pgTable('MyModel', {
|
|
520
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
521
|
-
name: text('name').notNull(),
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
static override relations = (): TRelationConfig[] => [];
|
|
525
|
-
}
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
### Repository Template
|
|
529
|
-
|
|
530
|
-
```typescript
|
|
531
|
-
import { DefaultCRUDRepository, repository } from '@venizia/ignis';
|
|
532
|
-
import { MyModel } from '@/models/entities';
|
|
533
|
-
import { PostgresDataSource } from '@/datasources/postgres.datasource';
|
|
534
|
-
|
|
535
|
-
@repository({ model: MyModel, dataSource: PostgresDataSource })
|
|
536
|
-
export class MyModelRepository extends DefaultCRUDRepository<typeof MyModel.schema> {}
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
> **Deep Dive:**
|
|
540
|
-
> - [BaseEntity Reference](../../references/base/models.md#baseentity-class)
|
|
541
|
-
> - [Schema Enrichers](../../references/base/models.md#schema-enrichers)
|
|
542
|
-
> - [BaseDataSource Reference](../../references/base/datasources.md)
|
|
543
|
-
> - [Repository Reference](../../references/base/repositories.md)
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Getting Started with Ignis
|
|
2
|
-
|
|
3
|
-
Welcome to Ignis! This guide takes you from zero to a fully functional, database-connected REST API.
|
|
4
|
-
|
|
5
|
-
**Recommended learning path:**
|
|
6
|
-
|
|
7
|
-
## 1. Prerequisites
|
|
8
|
-
|
|
9
|
-
Before starting, ensure you have the required tools installed.
|
|
10
|
-
|
|
11
|
-
[Check Prerequisites](./prerequisites.md)
|
|
12
|
-
|
|
13
|
-
## 2. Philosophy
|
|
14
|
-
|
|
15
|
-
Understand the "why" behind Ignis - how it combines enterprise-grade patterns with high performance.
|
|
16
|
-
|
|
17
|
-
[Learn the Philosophy](./philosophy.md)
|
|
18
|
-
|
|
19
|
-
## 3. Choose Your Starting Point
|
|
20
|
-
|
|
21
|
-
### Option A: 5-Minute Quickstart (Recommended for First-Timers)
|
|
22
|
-
|
|
23
|
-
Build your first "Hello World" endpoint in 5 minutes. No database, minimal setup.
|
|
24
|
-
|
|
25
|
-
[5-Minute Quickstart](./5-minute-quickstart.md) ⚡
|
|
26
|
-
|
|
27
|
-
### Option B: Complete Setup Guide
|
|
28
|
-
|
|
29
|
-
Full production setup with ESLint, Prettier, and proper project structure.
|
|
30
|
-
|
|
31
|
-
[Complete Setup Guide](./quickstart.md)
|
|
32
|
-
|
|
33
|
-
## 4. Building a CRUD API
|
|
34
|
-
|
|
35
|
-
**Most important guide** - build a complete Todo API covering:
|
|
36
|
-
- Models with Drizzle ORM
|
|
37
|
-
- DataSources & Repositories
|
|
38
|
-
- Controllers with auto-generated endpoints
|
|
39
|
-
- Dependency Injection
|
|
40
|
-
|
|
41
|
-
[Build a CRUD API](./building-a-crud-api.md)
|
|
42
|
-
|
|
43
|
-
## 5. Core Concepts
|
|
44
|
-
|
|
45
|
-
Deep dive into framework internals:
|
|
46
|
-
|
|
47
|
-
- [The Application Class](./core-concepts/application.md) - Lifecycle and configuration
|
|
48
|
-
- [Dependency Injection](./core-concepts/dependency-injection.md) - DI container patterns
|
|
49
|
-
- [Controllers](./core-concepts/controllers.md) - Routing and request handling
|
|
50
|
-
- [Services](./core-concepts/services.md) - Business logic layer
|
|
51
|
-
- [Persistent Layer](./core-concepts/persistent.md) - Models, DataSources, Repositories
|
|
52
|
-
- [Components](./core-concepts/components.md) - Reusable modules
|
|
53
|
-
|
|
54
|
-
## 6. Best Practices
|
|
55
|
-
|
|
56
|
-
Production-ready patterns and practices:
|
|
57
|
-
|
|
58
|
-
- [Architectural Patterns](./best-practices/architectural-patterns.md) - Layered architecture
|
|
59
|
-
- [API Usage Examples](./best-practices/api-usage-examples.md) - Real-world patterns
|
|
60
|
-
- [Code Style Standards](./best-practices/code-style-standards.md) - ESLint & Prettier
|
|
61
|
-
- [Security Guidelines](./best-practices/security-guidelines.md) - Protect your API
|
|
62
|
-
- [Performance Optimization](./best-practices/performance-optimization.md) - Speed improvements
|
|
63
|
-
- [Deployment Strategies](./best-practices/deployment-strategies.md) - Go to production
|
|
64
|
-
- [Troubleshooting Tips](./best-practices/troubleshooting-tips.md) - Common issues
|
|
65
|
-
- [Contribution Workflow](./best-practices/contribution-workflow.md) - Contribute to Ignis
|