@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,539 +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
|
-
### Modular Persistence with Components
|
|
422
|
-
|
|
423
|
-
Bundle related persistence resources into Components for better organization:
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
export class UserManagementComponent extends BaseComponent {
|
|
427
|
-
override binding() {
|
|
428
|
-
this.application.dataSource(PostgresDataSource);
|
|
429
|
-
this.application.repository(UserRepository);
|
|
430
|
-
this.application.repository(ProfileRepository);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
---
|
|
436
|
-
|
|
437
|
-
## 5. Transactions
|
|
438
|
-
|
|
439
|
-
Ignis supports explicit transaction objects that can be passed across multiple services and repositories, allowing for complex, multi-step business logic to be atomic.
|
|
440
|
-
|
|
441
|
-
### Using Transactions
|
|
442
|
-
|
|
443
|
-
To use transactions, start one from a repository or datasource, and then pass it to subsequent operations via the `options` parameter.
|
|
444
|
-
|
|
445
|
-
```typescript
|
|
446
|
-
// 1. Start a transaction
|
|
447
|
-
const tx = await userRepo.beginTransaction({
|
|
448
|
-
isolationLevel: 'SERIALIZABLE' // Optional, defaults to 'READ COMMITTED'
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
try {
|
|
452
|
-
// 2. Pass transaction to operations
|
|
453
|
-
// Create user
|
|
454
|
-
const user = await userRepo.create({
|
|
455
|
-
data: userData,
|
|
456
|
-
options: { transaction: tx }
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// Create profile (using same transaction)
|
|
460
|
-
await profileRepo.create({
|
|
461
|
-
data: { userId: user.id, ...profileData },
|
|
462
|
-
options: { transaction: tx }
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
// Call a service method (passing the transaction)
|
|
466
|
-
await orderService.createInitialOrder(user.id, { transaction: tx });
|
|
467
|
-
|
|
468
|
-
// 3. Commit the transaction
|
|
469
|
-
await tx.commit();
|
|
470
|
-
} catch (err) {
|
|
471
|
-
// 4. Rollback on error
|
|
472
|
-
await tx.rollback();
|
|
473
|
-
throw err;
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
### Isolation Levels
|
|
478
|
-
|
|
479
|
-
Ignis supports standard PostgreSQL isolation levels:
|
|
480
|
-
|
|
481
|
-
| Level | Description | Use Case |
|
|
482
|
-
|-------|-------------|----------|
|
|
483
|
-
| `READ COMMITTED` | (Default) Queries see only data committed before the query began. | General use, prevents dirty reads. |
|
|
484
|
-
| `REPEATABLE READ` | Queries see a snapshot as of the start of the transaction. | Reports, consistent reads across multiple queries. |
|
|
485
|
-
| `SERIALIZABLE` | Strictest level. Emulates serial execution. | Financial transactions, critical data integrity. |
|
|
486
|
-
|
|
487
|
-
### Best Practices
|
|
488
|
-
|
|
489
|
-
1. **Always use `try...catch...finally`**: Ensure `rollback()` is called on error to release the connection.
|
|
490
|
-
2. **Keep it short**: Long-running transactions hold database locks and connections.
|
|
491
|
-
3. **Pass explicit options**: When calling other services inside a transaction, ensure they accept and use the `transaction` option.
|
|
492
|
-
|
|
493
|
-
```typescript
|
|
494
|
-
// Service method supporting transactions
|
|
495
|
-
async createInitialOrder(userId: string, opts?: { transaction?: ITransaction }) {
|
|
496
|
-
return this.orderRepository.create({
|
|
497
|
-
data: { userId, status: 'PENDING' },
|
|
498
|
-
options: { transaction: opts?.transaction } // Forward the transaction
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
---
|
|
504
|
-
|
|
505
|
-
## Quick Reference
|
|
506
|
-
|
|
507
|
-
### Model Template
|
|
508
|
-
|
|
509
|
-
```typescript
|
|
510
|
-
import { BaseEntity, generateIdColumnDefs, model, TRelationConfig } from '@venizia/ignis';
|
|
511
|
-
import { pgTable, text } from 'drizzle-orm/pg-core';
|
|
512
|
-
|
|
513
|
-
@model({ type: 'entity' })
|
|
514
|
-
export class MyModel extends BaseEntity<typeof MyModel.schema> {
|
|
515
|
-
static override schema = pgTable('MyModel', {
|
|
516
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
517
|
-
name: text('name').notNull(),
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
static override relations = (): TRelationConfig[] => [];
|
|
521
|
-
}
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
### Repository Template
|
|
525
|
-
|
|
526
|
-
```typescript
|
|
527
|
-
import { DefaultCRUDRepository, repository } from '@venizia/ignis';
|
|
528
|
-
import { MyModel } from '@/models/entities';
|
|
529
|
-
import { PostgresDataSource } from '@/datasources/postgres.datasource';
|
|
530
|
-
|
|
531
|
-
@repository({ model: MyModel, dataSource: PostgresDataSource })
|
|
532
|
-
export class MyModelRepository extends DefaultCRUDRepository<typeof MyModel.schema> {}
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
> **Deep Dive:**
|
|
536
|
-
> - [BaseEntity Reference](../../references/base/models.md#baseentity-class)
|
|
537
|
-
> - [Schema Enrichers](../../references/base/models.md#schema-enrichers)
|
|
538
|
-
> - [BaseDataSource Reference](../../references/base/datasources.md)
|
|
539
|
-
> - [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
|