@venizia/ignis-docs 0.0.1-9 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -0
- package/package.json +2 -2
- package/wiki/changelogs/{v0.0.1-7-initial-architecture.md → 2025-12-16-initial-architecture.md} +20 -12
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +300 -0
- package/wiki/changelogs/2025-12-17-refactor.md +80 -12
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +28 -90
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +101 -297
- package/wiki/changelogs/index.md +20 -8
- package/wiki/changelogs/planned-schema-migrator.md +561 -0
- package/wiki/changelogs/planned-transaction-support.md +216 -0
- package/wiki/changelogs/template.md +123 -0
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -2
- package/wiki/get-started/best-practices/architectural-patterns.md +2 -2
- package/wiki/get-started/best-practices/code-style-standards.md +575 -10
- package/wiki/get-started/best-practices/common-pitfalls.md +5 -3
- package/wiki/get-started/best-practices/contribution-workflow.md +2 -0
- package/wiki/get-started/best-practices/data-modeling.md +91 -34
- package/wiki/get-started/best-practices/security-guidelines.md +3 -1
- package/wiki/get-started/building-a-crud-api.md +3 -3
- package/wiki/get-started/core-concepts/application.md +72 -3
- package/wiki/get-started/core-concepts/bootstrapping.md +566 -0
- package/wiki/get-started/core-concepts/components.md +4 -2
- package/wiki/get-started/core-concepts/persistent.md +350 -378
- package/wiki/get-started/core-concepts/services.md +21 -27
- package/wiki/references/base/bootstrapping.md +789 -0
- package/wiki/references/base/components.md +1 -1
- package/wiki/references/base/dependency-injection.md +95 -2
- package/wiki/references/base/services.md +2 -2
- package/wiki/references/components/authentication.md +4 -3
- package/wiki/references/components/index.md +1 -1
- package/wiki/references/helpers/error.md +2 -2
- package/wiki/references/src-details/boot.md +379 -0
- package/wiki/references/src-details/core.md +2 -2
- package/wiki/changelogs/v0.0.1-8-model-repo-datasource-refactor.md +0 -278
|
@@ -6,26 +6,24 @@ Ignis streamlines data modeling with Drizzle ORM by providing powerful helpers a
|
|
|
6
6
|
|
|
7
7
|
All entity models should extend `BaseEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
|
|
8
8
|
|
|
9
|
-
**
|
|
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`):**
|
|
10
12
|
|
|
11
13
|
```typescript
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
model,
|
|
15
|
-
TTableObject,
|
|
16
|
-
} from '@venizia/ignis';
|
|
17
|
-
import { configurationTable, configurationRelations } from './schema'; // Your Drizzle schema
|
|
14
|
+
import { BaseEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
|
|
15
|
+
import { pgTable } from 'drizzle-orm/pg-core';
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
|
|
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
|
+
});
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Use static properties (recommended pattern)
|
|
26
|
-
static override schema = configurationTable;
|
|
27
|
-
static override relations = () => configurationRelations.definitions;
|
|
28
|
-
static override TABLE_NAME = 'Configuration';
|
|
25
|
+
// 2. Define relations as a static method (return empty array if none)
|
|
26
|
+
static override relations = () => [];
|
|
29
27
|
}
|
|
30
28
|
```
|
|
31
29
|
|
|
@@ -41,7 +39,7 @@ Instead of manually defining common columns like primary keys, timestamps, or au
|
|
|
41
39
|
| `generateTzColumnDefs` | Adds timestamps | `createdAt`, `modifiedAt` (auto-updating) |
|
|
42
40
|
| `generateUserAuditColumnDefs` | Adds audit fields | `createdBy`, `modifiedBy` |
|
|
43
41
|
| `generateDataTypeColumnDefs` | Adds generic value fields | `nValue` (number), `tValue` (text), `jValue` (json), etc. |
|
|
44
|
-
| `
|
|
42
|
+
| `extraUserColumns` | Comprehensive user fields | Combines audit, timestamps, status, and type fields |
|
|
45
43
|
|
|
46
44
|
**Usage Example:**
|
|
47
45
|
|
|
@@ -82,39 +80,98 @@ export const configurationTable = pgTable(
|
|
|
82
80
|
|
|
83
81
|
## 3. Defining Relations
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
Relations are defined using the `TRelationConfig` structure within the static `relations` method of your model.
|
|
86
84
|
|
|
87
|
-
**Example:**
|
|
85
|
+
**Example (`src/models/entities/configuration.model.ts`):**
|
|
88
86
|
|
|
89
87
|
```typescript
|
|
90
|
-
import {
|
|
91
|
-
|
|
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 ...
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
relations: [
|
|
100
|
+
// Define relations
|
|
101
|
+
static override relations = (): TRelationConfig[] => [
|
|
96
102
|
{
|
|
97
103
|
name: 'creator',
|
|
98
104
|
type: RelationTypes.ONE,
|
|
99
|
-
schema:
|
|
105
|
+
schema: User.schema,
|
|
100
106
|
metadata: {
|
|
101
|
-
fields: [
|
|
102
|
-
references: [
|
|
107
|
+
fields: [Configuration.schema.createdBy],
|
|
108
|
+
references: [User.schema.id],
|
|
103
109
|
},
|
|
104
110
|
},
|
|
105
|
-
]
|
|
106
|
-
}
|
|
111
|
+
];
|
|
112
|
+
}
|
|
107
113
|
```
|
|
108
114
|
|
|
109
|
-
|
|
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.
|
|
110
122
|
|
|
111
123
|
```typescript
|
|
112
|
-
|
|
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)**
|
|
113
148
|
|
|
114
|
-
|
|
149
|
+
For most repositories, you don't need a constructor. The DataSource is automatically injected.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
115
152
|
@repository({ model: Configuration, dataSource: PostgresDataSource })
|
|
116
153
|
export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {
|
|
117
|
-
// No constructor needed!
|
|
118
|
-
// from the @repository decorator and entity's static properties
|
|
154
|
+
// No constructor needed!
|
|
119
155
|
}
|
|
120
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
|
+
```
|
|
@@ -72,9 +72,11 @@ const SecureRoute = {
|
|
|
72
72
|
|
|
73
73
|
**Access user in protected routes:**
|
|
74
74
|
```typescript
|
|
75
|
+
import { Authentication, IJWTTokenPayload, ApplicationError, getError } from '@venizia/ignis';
|
|
76
|
+
|
|
75
77
|
const user = c.get(Authentication.CURRENT_USER) as IJWTTokenPayload;
|
|
76
78
|
if (!user.roles.includes('admin')) {
|
|
77
|
-
throw
|
|
79
|
+
throw getError({ statusCode: 403, message: 'Forbidden' });
|
|
78
80
|
}
|
|
79
81
|
```
|
|
80
82
|
|
|
@@ -645,7 +645,7 @@ For complex validation or business rules, create a Service layer:
|
|
|
645
645
|
|
|
646
646
|
```typescript
|
|
647
647
|
// src/services/todo.service.ts
|
|
648
|
-
import { BaseService, inject } from '@venizia/ignis';
|
|
648
|
+
import { BaseService, inject, getError } from '@venizia/ignis';
|
|
649
649
|
import { TodoRepository } from '@/repositories/todo.repository';
|
|
650
650
|
|
|
651
651
|
export class TodoService extends BaseService {
|
|
@@ -659,7 +659,7 @@ export class TodoService extends BaseService {
|
|
|
659
659
|
async createTodo(data: any) {
|
|
660
660
|
// Business logic validation
|
|
661
661
|
if (data.title.length < 3) {
|
|
662
|
-
throw
|
|
662
|
+
throw getError({ message: 'Title too short' });
|
|
663
663
|
}
|
|
664
664
|
|
|
665
665
|
// Check for duplicates
|
|
@@ -667,7 +667,7 @@ export class TodoService extends BaseService {
|
|
|
667
667
|
filter: { where: { title: data.title } },
|
|
668
668
|
});
|
|
669
669
|
if (existing) {
|
|
670
|
-
throw
|
|
670
|
+
throw getError({ message: 'Todo already exists' });
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
return this.todoRepository.create({ data });
|
|
@@ -42,10 +42,13 @@ export class Application extends BaseApplication {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
preConfigure(): ValueOrPromise<void> {
|
|
45
|
-
//
|
|
45
|
+
// Manual registration (traditional approach)
|
|
46
46
|
this.dataSource(MyDataSource);
|
|
47
47
|
this.service(MyService);
|
|
48
48
|
this.controller(MyController);
|
|
49
|
+
|
|
50
|
+
// Or use boot system for auto-discovery (recommended for larger apps)
|
|
51
|
+
// See Bootstrapping section below
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
postConfigure(): ValueOrPromise<void> {
|
|
@@ -75,10 +78,74 @@ The `BaseApplication` class provides several **overridable hook methods** that a
|
|
|
75
78
|
| :--- | :--- |
|
|
76
79
|
| `getAppInfo()` | **Required.** Return application metadata, usually from `package.json`. Used for OpenAPI docs. |
|
|
77
80
|
| `staticConfigure()` | Configure static file serving. |
|
|
78
|
-
| `preConfigure()` | **Most Important Hook.** Set up application resources like components, controllers, services, and datasources. |
|
|
81
|
+
| `preConfigure()` | **Most Important Hook.** Set up application resources like components, controllers, services, and datasources. Can be skipped if using boot system. |
|
|
79
82
|
| `postConfigure()` | Perform actions *after* all resources have been configured and instantiated. |
|
|
80
83
|
| `setupMiddlewares()`| Add custom application-level middlewares to the Hono instance. |
|
|
81
84
|
|
|
85
|
+
## Bootstrapping (Auto-discovery)
|
|
86
|
+
|
|
87
|
+
The boot system provides automatic artifact discovery and loading, eliminating manual registration. When enabled, it scans your project directory and automatically loads controllers, services, repositories, and datasources.
|
|
88
|
+
|
|
89
|
+
> **Detailed Guide:** See [Bootstrapping Concepts](./bootstrapping.md) for complete documentation.
|
|
90
|
+
|
|
91
|
+
### Enabling Boot System
|
|
92
|
+
|
|
93
|
+
Add `bootOptions` to your application config:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
export const appConfigs: IApplicationConfigs = {
|
|
97
|
+
host: process.env.APP_ENV_SERVER_HOST,
|
|
98
|
+
port: +(process.env.APP_ENV_SERVER_PORT ?? 3000),
|
|
99
|
+
// Enable boot system
|
|
100
|
+
bootOptions: {
|
|
101
|
+
datasources: { dirs: ['datasources'] },
|
|
102
|
+
repositories: { dirs: ['repositories'] },
|
|
103
|
+
services: { dirs: ['services'] },
|
|
104
|
+
controllers: { dirs: ['controllers'] }
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
With boot enabled, you can skip manual registration in `preConfigure()`:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
export class Application extends BaseApplication {
|
|
113
|
+
// No need to register artifacts manually!
|
|
114
|
+
// Boot system handles it automatically
|
|
115
|
+
|
|
116
|
+
preConfigure(): ValueOrPromise<void> {
|
|
117
|
+
// Only register things that need custom configuration
|
|
118
|
+
// Everything else is auto-discovered
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Boot vs Manual Registration
|
|
124
|
+
|
|
125
|
+
| Approach | Use Case | Pros | Cons |
|
|
126
|
+
|----------|----------|------|------|
|
|
127
|
+
| **Boot System** | Apps with 10+ artifacts per type | Auto-discovery, scalable, clean code | Requires file naming conventions |
|
|
128
|
+
| **Manual Registration** | Small apps (< 5 artifacts) | Fine-grained control, explicit | Tedious, maintenance burden |
|
|
129
|
+
|
|
130
|
+
### Project Structure for Boot
|
|
131
|
+
|
|
132
|
+
Follow naming conventions for auto-discovery:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
src/
|
|
136
|
+
├── datasources/
|
|
137
|
+
│ └── postgres.datasource.js
|
|
138
|
+
├── repositories/
|
|
139
|
+
│ ├── user.repository.js
|
|
140
|
+
│ └── product.repository.js
|
|
141
|
+
├── services/
|
|
142
|
+
│ ├── auth.service.js
|
|
143
|
+
│ └── user.service.js
|
|
144
|
+
└── controllers/
|
|
145
|
+
├── auth.controller.js
|
|
146
|
+
└── user.controller.js
|
|
147
|
+
```
|
|
148
|
+
|
|
82
149
|
## Lifecycle Diagram
|
|
83
150
|
|
|
84
151
|
This diagram shows the sequence of operations during application startup. The methods you can override are highlighted.
|
|
@@ -90,7 +157,8 @@ graph TD
|
|
|
90
157
|
|
|
91
158
|
subgraph "initialize() Sequence"
|
|
92
159
|
direction TB
|
|
93
|
-
B -->
|
|
160
|
+
B --> B0["boot() - if bootOptions configured"];
|
|
161
|
+
B0 --> B1["printStartUpInfo"];
|
|
94
162
|
B1 --> B2["validateEnvs"];
|
|
95
163
|
B2 --> B3["registerDefaultMiddlewares"];
|
|
96
164
|
B3 --> B4["staticConfigure()"];
|
|
@@ -129,6 +197,7 @@ Application configuration is passed to the `BaseApplication` constructor via an
|
|
|
129
197
|
| `path.isStrict`| `boolean`| `true` | If `true`, the router is strict about trailing slashes. |
|
|
130
198
|
| `debug.showRoutes`| `boolean`| `false`| If `true`, prints all registered routes to the console on startup. |
|
|
131
199
|
| `favicon` | `string` | `'🔥'` | An emoji to be used as the application's favicon. |
|
|
200
|
+
| `bootOptions` | `IBootOptions` | `undefined` | Enable auto-discovery of artifacts. See [Bootstrapping](./bootstrapping.md). |
|
|
132
201
|
|
|
133
202
|
### Example Configuration
|
|
134
203
|
|