@venizia/ignis-docs 0.2.0 → 0.2.1-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.
Files changed (142) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +22 -11
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +6 -2
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +182 -93
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -1,222 +1,22 @@
1
1
  ---
2
- title: DataSources Reference
3
- description: Technical reference for the engine-neutral DataSource contract and the PostgreSQL connector implementation
2
+ title: DataSources
3
+ description: Manage the connection to a database or search engine with a DataSource class
4
4
  difficulty: intermediate
5
5
  ---
6
6
 
7
- # Deep Dive: DataSources
7
+ # DataSources
8
8
 
9
- Technical reference for DataSource classes - managing database and search engine connections in IGNIS.
9
+ A datasource owns the connection to a database (or search engine).
10
10
 
11
- > [!IMPORTANT] Base vs. Connectors
12
- > IGNIS splits datasources into an **engine-neutral root** (`src/base/datasources/`) and **per-engine connectors** (`src/connectors/{postgres,typesense}/datasources/`). `AbstractDataSource` has no SQL, no Drizzle, and no `pool` - those live only in the PostgreSQL connector. See [Connectors](./connectors) for the full base-vs-connectors architecture, dual-door exports, and how to add a new engine. This page documents the neutral contract plus the PostgreSQL connector in depth; see [Search & Typesense](/guides/core-concepts/persistent/search-typesense) for the other engine.
11
+ ## In one example
13
12
 
14
- **Files:** `packages/core/src/base/datasources/*.ts` (neutral) and `packages/core/src/connectors/postgres/datasources/*.ts` (PostgreSQL)
15
-
16
- ## Quick Reference
17
-
18
- | Class/Interface | Purpose | Key Members |
19
- |-----------------|---------|-------------|
20
- | **IDataSource** | Engine-neutral contract for all datasources | `name`, `settings`, `schema`, `getSchema()`, `getSettings()`, `configure()` |
21
- | **AbstractDataSource** | Engine-neutral base implementation with logging | Extends `BaseHelper`, `getCapabilities()` defaults to `{ transactions: false }`, `beginTransaction()` defaults to `throwNotSupported(...)` |
22
- | **AbstractPostgresDataSource** | PostgreSQL-aware abstraction | Adds `connector`, `client`, `driver`, abstract `getConnectionString()`/`beginTransaction()` |
23
- | **BasePostgresDataSource** | Concrete class to extend for PostgreSQL | Auto-discovery, real transaction support, constructor with config. Canonical name - `BaseDataSource` is a compatibility alias re-exporting the same class |
24
- | **ITransaction** | Engine-neutral transaction contract | `isActive`, `commit()`, `rollback()` (no connector field) |
25
- | **IDatabaseTransaction** | PostgreSQL transaction object | Extends `ITransaction`, adds `connector`, `isolationLevel` |
26
- | **IsolationLevels** | Isolation level constants (PostgreSQL) | `READ_COMMITTED`, `REPEATABLE_READ`, `SERIALIZABLE` |
27
-
28
- ## `IDataSource` Interface
29
-
30
- Engine-neutral contract implemented by every datasource in the framework, regardless of engine.
31
-
32
- **File:** `packages/core/src/base/datasources/common/types.ts`
33
-
34
- ### Type Parameters
35
-
36
- ```typescript
37
- interface IDataSource<
38
- Settings extends object = {},
39
- Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
40
- ConfigurableOptions extends object = {},
41
- > extends IConfigurable<ConfigurableOptions>
42
- ```
43
-
44
- | Type Parameter | Default | Description |
45
- |----------------|---------|-------------|
46
- | `Settings` | `{}` | Connection configuration type (host, port, etc.) |
47
- | `Schema` | `TAnyDataSourceSchema` | Combined schema type (shape depends on the connector - Drizzle tables for PostgreSQL, collection definitions for typesense) |
48
- | `ConfigurableOptions` | `{}` | Options passed to `configure()` |
49
-
50
- ### Properties & Methods
51
-
52
- | Member | Type | Description |
53
- |--------|------|-------------|
54
- | `name` | `string` | Datasource name |
55
- | `settings` | `Settings` | Configuration object |
56
- | `schema` | `Schema` | Combined schema (auto-discovered or manual) |
57
- | `getSchema()` | `Schema` | Returns combined schema |
58
- | `getSettings()` | `Settings` | Returns connection settings |
59
- | `configure(opts?)` | `ValueOrPromise<void>` | Initializes the underlying connection (from `IConfigurable`) |
60
-
61
- > [!NOTE]
62
- > `getCapabilities()` and `beginTransaction()` are not part of the `IDataSource` interface - they are declared on `AbstractDataSource` (below), which every connector extends.
63
-
64
- ## `AbstractDataSource` (Engine-Neutral Root)
65
-
66
- **File:** `packages/core/src/base/datasources/abstract.ts`
67
-
68
- Top-level abstract class implemented by every engine. Extends `BaseHelper` for scoped logging. Contains **no SQL, no Drizzle, and no connection-pool members** - those are added by each connector.
69
-
70
- ```typescript
71
- abstract class AbstractDataSource<
72
- Settings extends object = {},
73
- Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
74
- ConfigurableOptions extends object = {},
75
- > extends BaseHelper implements IDataSource<Settings, Schema, ConfigurableOptions>
76
- ```
77
-
78
- **Properties:**
79
-
80
- | Property | Type | Visibility | Description |
81
- |----------|------|------------|-------------|
82
- | `name` | `string` | public | Datasource identifier |
83
- | `settings` | `Settings` | public | Connection configuration |
84
- | `schema` | `Schema` | public | Combined schema |
85
-
86
- **Abstract methods** (must be implemented by connectors):
87
-
88
- | Method | Return Type | Description |
89
- |--------|-------------|-------------|
90
- | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the underlying connection |
91
- | `getConnectionString()` | `ValueOrPromise<string>` | Return the connection URL |
92
-
93
- **Concrete methods (defaults, overridable by connectors):**
94
-
95
- | Method | Return Type | Default Behavior |
96
- |--------|-------------|-------------------|
97
- | `getSettings()` | `Settings` | Returns `this.settings` |
98
- | `getSchema()` | `Schema` | Returns `this.schema` (throws if not initialized) |
99
- | `getCapabilities()` | `IDataSourceCapabilities` | Returns `{ transactions: false }` |
100
- | `beginTransaction(opts?)` | `Promise<ITransaction>` | Calls `throwNotSupported({ scope: this.constructor.name, feature: 'Transactions', logger: this.logger })` - throws HTTP 501 with `messageCode: 'core.not_supported'` |
101
-
102
- > [!NOTE] NotSupported convention
103
- > Every capability an engine doesn't implement - transactions, row-level locking - uses the same `throwNotSupported` utility (`packages/core/src/utilities/error.utility.ts`), producing a consistent `501 Not Implemented` with `messageCode: 'core.not_supported'`. This is how the typesense connector signals "not applicable to this engine" instead of silently no-op-ing.
104
-
105
- ### `IDataSourceCapabilities`
13
+ The smallest real datasource: a `pg.Pool` wrapped in a class, wired to node-postgres via `@datasource`.
106
14
 
107
15
  ```typescript
108
- interface IDataSourceCapabilities {
109
- transactions: boolean;
110
- }
111
- ```
112
-
113
- Only `BasePostgresDataSource` overrides `getCapabilities()` to return `{ transactions: true }`. The typesense datasources inherit the neutral default.
114
-
115
- ## PostgreSQL Connector: `AbstractPostgresDataSource` & `BasePostgresDataSource`
116
-
117
- **Files:** `packages/core/src/connectors/postgres/datasources/abstract.ts`, `packages/core/src/connectors/postgres/datasources/base.ts`
118
-
119
- ### `AbstractPostgresDataSource`
120
-
121
- Extends `AbstractDataSource` with PostgreSQL/Drizzle-specific members.
122
-
123
- ```typescript
124
- abstract class AbstractPostgresDataSource<
125
- Settings extends object = {},
126
- Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
127
- ConfigurableOptions extends object = {},
128
- > extends AbstractDataSource<Settings, Schema, ConfigurableOptions>
129
- ```
130
-
131
- **Additional properties:**
132
-
133
- | Property | Type | Visibility | Description |
134
- |----------|------|------------|-------------|
135
- | `connector` | `TRelationalConnector<Schema>` | public | Drizzle ORM instance (any Drizzle pg driver satisfies this - see the driver seam below) |
136
- | `driver` | `IRelationalDriver` | protected | The connection driver (`node-postgres` or `postgres-js`); built lazily by `wireDriverFromMetadata()` from the class named in `@datasource({ driver })`, or explicitly by `useDriver()` |
137
- | `client` | `Client` (`Pool` by default) | protected | The raw driver client `configure()` built - a `pg.Pool`, or a postgres-js `Sql`. Assigning it alone is enough: `wireDriverFromMetadata()` instantiates the `@datasource({ driver })` class over it on first use. Absent once `useDriver()` wired a driver instead |
138
-
139
- > [!NOTE] Driver seam
140
- > `AbstractRelationalDataSource`/`BaseRelationalDataSource` (exported as `AbstractPostgresDataSource`/`BasePostgresDataSource`) now take a fourth generic - `<Settings, Schema, ConfigurableOptions, Client = Pool>` - so a `postgres-js` datasource can declare `Client = Sql` and keep `getClient()` honest. `@datasource({ driver })` names the driver **class** (`NodePostgresDriver` or `PostgresJsDriver`), never a string - a driver-name string cannot carry `pg`/`postgres` into the app's bundle, only a real class reference can. `configure()` only needs to assign `this.client`; the protected `wireDriverFromMetadata()` (called internally by `getConnector()`/`resolveDriver()`) instantiates the named class over it and builds `this.connector`, lazily and idempotently. The protected `useDriver({ driver, schema? })` stays available for a custom or third-party driver - it assigns `this.driver` **and** builds `this.connector` in one step, bypassing `@datasource({ driver })` entirely. `pg` and `postgres` are both optional peer dependencies; concrete drivers live at `@venizia/ignis/postgres/node-postgres` and `@venizia/ignis/postgres/postgres-js`, and Supabase support at `@venizia/ignis/postgres/supabase`. See [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers).
141
-
142
- **Additional abstract method:**
143
-
144
- | Method | Return Type | Description |
145
- |--------|-------------|-------------|
146
- | `beginTransaction(opts?)` | `Promise<IDatabaseTransaction<Schema>>` | Start a new PostgreSQL transaction |
147
-
148
- ### `BasePostgresDataSource` (canonical name; `BaseDataSource` is a compatibility alias)
149
-
150
- Extends `AbstractPostgresDataSource` with a constructor, **schema auto-discovery**, and a real `beginTransaction()` implementation backed by the connection pool.
151
-
152
- ```typescript
153
- abstract class BasePostgresDataSource<
154
- Settings extends object = {},
155
- Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
156
- ConfigurableOptions extends object = {},
157
- > extends AbstractPostgresDataSource<Settings, Schema, ConfigurableOptions>
158
- ```
159
-
160
- > [!TIP] Naming
161
- > `BasePostgresDataSource` is the canonical, engine-carrying name - prefer it in new code. `import { BaseDataSource } from '@venizia/ignis'` (or `@venizia/ignis/postgres`) still resolves to the exact same class via a re-export in `connectors/postgres/datasources/index.ts` (`export { BasePostgresDataSource as BaseDataSource } from './base-datasource'`), so existing code is unaffected.
162
-
163
- #### Key Features
164
-
165
- | Feature | Description |
166
- |---------|--------------|
167
- | **Schema Auto-Discovery** | Schema is automatically built from registered `@repository` decorators |
168
- | **Manual Override** | You can manually provide schema in constructor for full control |
169
- | **Built-in Transaction Support** | `beginTransaction()` acquires its connection from the resolved driver; overrides `getCapabilities()` to return `{ transactions: true }` |
170
-
171
- > [!TIP]
172
- > Set `autoDiscovery` to `false` in the `@datasource` decorator to disable automatic schema discovery. This is useful when you want to manually provide the schema.
173
-
174
- ### Constructor Options
175
-
176
- ```typescript
177
- constructor(opts: {
178
- name: string; // DataSource name (usually class name)
179
- config: Settings; // Database connection settings
180
- schema?: Schema; // Optional - auto-discovered if not provided
181
- })
182
- ```
183
-
184
- ### Schema Auto-Discovery
185
-
186
- When you use `@repository({ model: YourModel, dataSource: YourDataSource })`, the framework automatically:
187
-
188
- 1. Registers the model-datasource binding in the MetadataRegistry
189
- 2. When `getSchema()` is called, discovers all models bound to this datasource
190
- 3. Builds the combined schema (tables + relations) automatically
191
-
192
- **This means you no longer need to manually merge tables and relations in the DataSource constructor!**
193
-
194
- ### Configuration Flow
195
-
196
- 1. **Your DataSource's `constructor` is called**:
197
- - You call `super()` with `name` and `config`
198
- - Schema is auto-discovered from `@repository` bindings (or manually provided via `schema`)
199
-
200
- 2. **`Application.registerDataSources()` is called during startup**:
201
- - The application gets your `DataSource` instance from the DI container
202
- - It calls the `configure()` method on your instance
203
-
204
- 3. **Your `configure()` method runs**:
205
- - Create a `Pool` instance and assign it to `this.client` - that is the whole method
206
- - `getConnector()`/`beginTransaction()` lazily instantiate the class named in `@datasource({ driver })` over `this.client` and build the Drizzle connector from it - your `configure()` never touches `this.connector` directly
207
-
208
- ### Example Implementations
209
-
210
- #### Pattern 1: Auto-Discovery (Recommended)
211
-
212
- Simplest approach - schema is auto-discovered from repositories:
213
-
214
- ```typescript
215
- // src/datasources/postgres.datasource.ts
216
- import { BasePostgresDataSource, datasource } from '@venizia/ignis';
217
- import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
218
- import { applicationEnvironment, int, ValueOrPromise } from '@venizia/ignis-helpers';
219
16
  import { Pool } from 'pg';
17
+ import { datasource } from '@venizia/ignis';
18
+ import { BasePostgresDataSource } from '@venizia/ignis/postgres';
19
+ import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
220
20
 
221
21
  interface IDataSourceConfigs {
222
22
  host: string;
@@ -224,302 +24,142 @@ interface IDataSourceConfigs {
224
24
  database: string;
225
25
  user: string;
226
26
  password: string;
227
- ssl: boolean;
228
27
  }
229
28
 
230
- /**
231
- * PostgresDataSource with auto-discovery support.
232
- *
233
- * How it works:
234
- * 1. @repository decorator binds model to datasource
235
- * 2. getSchema() auto-discovers all bound models when the driver/connector are wired
236
- * 3. Naming NodePostgresDriver in @datasource is what wires the driver and Drizzle connector
237
- */
238
29
  @datasource({ driver: NodePostgresDriver })
239
30
  export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
240
- private readonly protocol = 'postgresql';
241
-
242
- constructor() {
243
- super({
244
- name: PostgresDataSource.name,
245
- config: {
246
- host: applicationEnvironment.get<string>('APP_ENV_POSTGRES_HOST'),
247
- port: int(applicationEnvironment.get<string>('APP_ENV_POSTGRES_PORT')),
248
- database: applicationEnvironment.get<string>('APP_ENV_POSTGRES_DATABASE'),
249
- user: applicationEnvironment.get<string>('APP_ENV_POSTGRES_USERNAME'),
250
- password: applicationEnvironment.get<string>('APP_ENV_POSTGRES_PASSWORD'),
251
- ssl: false,
252
- },
253
- // NO schema property - auto-discovered from @repository bindings!
254
- });
255
- }
256
-
257
- override configure(): ValueOrPromise<void> {
258
- const schema = Object.keys(this.getSchema());
259
- this.logger.debug(
260
- '[configure] Auto-discovered schema | Schema + Relations (%s): %o',
261
- schema.length,
262
- schema,
263
- );
264
-
265
- // That is all - the base class wires the driver + connector from @datasource({ driver }).
31
+ override configure(): void {
266
32
  this.client = new Pool(this.settings);
267
33
  }
268
34
 
269
- override getConnectionString(): ValueOrPromise<string> {
35
+ override getConnectionString(): string {
270
36
  const { host, port, user, password, database } = this.settings;
271
- return `${this.protocol}://${user}:${password}@${host}:${port}/${database}`;
37
+ return `postgresql://${user}:${password}@${host}:${port}/${database}`;
272
38
  }
273
39
  }
274
40
  ```
275
41
 
276
- With this pattern, when you define repositories:
42
+ A `@repository` binds a model to `PostgresDataSource`, and the schema is auto-discovered from that binding - no manual schema wiring.
277
43
 
278
- ```typescript
279
- @repository({ model: User, dataSource: PostgresDataSource })
280
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
44
+ ## How it works
281
45
 
282
- @repository({ model: Configuration, dataSource: PostgresDataSource })
283
- export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {}
284
- ```
46
+ - **Driver is a class, not a string.** `@datasource({ driver })` names `NodePostgresDriver` or `PostgresJsDriver` as a class reference - never a driver-name string. Only a real class reference carries `pg`/`postgres` into the application's bundle, so both stay genuinely optional peer dependencies.
47
+ - **`configure()` has exactly one job.** Build the raw client and assign it to `this.client` (a `pg.Pool` for node-postgres, or a postgres-js `Sql`). It never touches `this.connector` directly.
48
+ - **The driver wires lazily.** The first time `getConnector()` or `beginTransaction()` is called, the base class reads the class named in `@datasource({ driver })`, instantiates it over `this.client`, and builds `this.connector` from it.
49
+ - **Base vs. connector split.** IGNIS splits datasources into an engine-neutral root (`AbstractDataSource` - no SQL, no Drizzle, no pool) and per-engine connectors (`BasePostgresDataSource` for PostgreSQL, a parallel class for typesense). See [Connectors](/references/base/connectors) for the full architecture.
50
+ - **Naming.** The PostgreSQL connector's canonical class is `BaseRelationalDataSource`; `BasePostgresDataSource` and `BaseDataSource` are compatibility aliases re-exporting the same class.
285
51
 
286
- The `PostgresDataSource.schema` will automatically include User and Configuration tables and their relations.
52
+ ## Common tasks
287
53
 
288
- #### Pattern 2: Manual Schema (Full Control)
54
+ ### Configure connection settings
289
55
 
290
- When you need explicit control over schema (e.g., subset of models, custom ordering):
56
+ Pass connection settings through the constructor's `config` option. `configure()` reads them back off `this.settings`.
291
57
 
292
58
  ```typescript
293
- import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
294
- import {
295
- User, userTable, userRelations,
296
- Configuration, configurationTable, configurationRelations,
297
- } from '@/models/entities';
59
+ import { Pool } from 'pg';
60
+ import { applicationEnvironment, int } from '@venizia/ignis-helpers';
298
61
 
299
- @datasource({ driver: NodePostgresDriver })
300
62
  export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
301
63
  constructor() {
302
64
  super({
303
65
  name: PostgresDataSource.name,
304
66
  config: {
305
- host: process.env.APP_ENV_POSTGRES_HOST ?? 'localhost',
306
- port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
307
- database: process.env.APP_ENV_POSTGRES_DATABASE ?? 'mydb',
308
- user: process.env.APP_ENV_POSTGRES_USERNAME ?? 'postgres',
309
- password: process.env.APP_ENV_POSTGRES_PASSWORD ?? '',
310
- },
311
- // Manually provide schema using spread syntax
312
- schema: {
313
- [User.TABLE_NAME]: userTable,
314
- [Configuration.TABLE_NAME]: configurationTable,
315
- ...userRelations.relations,
316
- ...configurationRelations.relations,
67
+ host: applicationEnvironment.get<string>('APP_ENV_POSTGRES_HOST'),
68
+ port: int(applicationEnvironment.get<string>('APP_ENV_POSTGRES_PORT')),
69
+ database: applicationEnvironment.get<string>('APP_ENV_POSTGRES_DATABASE'),
70
+ user: applicationEnvironment.get<string>('APP_ENV_POSTGRES_USERNAME'),
71
+ password: applicationEnvironment.get<string>('APP_ENV_POSTGRES_PASSWORD'),
317
72
  },
318
73
  });
319
74
  }
320
75
 
321
- override configure(): ValueOrPromise<void> {
322
- // Manually-provided schema is used as-is by the connector the base class builds from this.client
76
+ override configure(): void {
323
77
  this.client = new Pool(this.settings);
324
78
  }
325
-
326
- override getConnectionString(): ValueOrPromise<string> {
327
- // ...
328
- }
329
79
  }
330
80
  ```
331
81
 
332
- > [!IMPORTANT]
333
- > Your `configure()` must leave the datasource with a way to reach the database: either assign the raw client to `this.client` (paired with naming the driver class in `@datasource({ driver })`), or wire a driver directly with `this.useDriver({ driver })` for a custom or third-party driver. `getConnector()`/`beginTransaction()` resolve the driver lazily from whichever you provided. With neither, it throws `No driver and no client`.
82
+ ### Choose a driver
334
83
 
335
- ### `@datasource` Decorator
84
+ | Driver | Package | When to use |
85
+ |---|---|---|
86
+ | `NodePostgresDriver` | `pg` | Long-standing default |
87
+ | `PostgresJsDriver` | `postgres` | Required for Supabase's transaction pooler; faster elsewhere |
336
88
 
337
- The `@datasource` decorator registers datasource metadata:
89
+ Swapping drivers only changes which class `@datasource` names and how `configure()` builds the client:
338
90
 
339
91
  ```typescript
340
- @datasource({
341
- driver: NodePostgresDriver, // Required - driver CLASS (or a search engine's driver-name string)
342
- autoDiscovery?: true // Optional - defaults to true
343
- })
344
- ```
345
-
346
- | Option | Type | Default | Description |
347
- |--------|------|---------|-------------|
348
- | `driver` | `TDataSourceDriverClass` | - | The driver **class** - `NodePostgresDriver` or `PostgresJsDriver` (imported from `@venizia/ignis/postgres/node-postgres` / `.../postgres-js`), never a driver-name string. A class reference is the only thing that carries `pg`/`postgres` into the app's bundle. **Omit it for a search datasource**: `extends TypesenseDataSource` already names the engine, and is what carries `typesense` into the bundle |
349
- | `autoDiscovery` | `boolean` | `true` | Enable/disable schema auto-discovery |
350
-
351
- ### Abstract Methods
352
-
353
- When extending `BasePostgresDataSource`, these methods must be implemented:
354
-
355
- | Method | Return Type | Description |
356
- |--------|-------------|-------------|
357
- | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the client. Must set `this.client` (the base class wires the driver and Drizzle connector from `@datasource({ driver })`), or call `this.useDriver()` directly for a custom driver. |
358
- | `getConnectionString()` | `ValueOrPromise<string>` | Return the database connection string. |
359
-
360
- ### Helper Methods
361
-
362
- | Method | Description |
363
- |--------|-------------|
364
- | `getSchema()` | Returns the schema (auto-discovers via `discoverSchema()` if not manually provided) |
365
- | `getSettings()` | Returns connection settings |
366
- | `getConnector()` | Returns the Drizzle connector |
367
- | `hasDiscoverableModels()` | Returns `true` if there are models registered for this datasource via `@repository` |
368
-
369
- ### Protected Methods
370
-
371
- | Method | Description |
372
- |--------|-------------|
373
- | `discoverSchema()` | Queries the `MetadataRegistry` for all `@repository` bindings targeting this datasource, then calls `registry.buildSchema()` to merge tables and relations into a single schema object. |
374
-
375
- ## Connector Types
376
-
377
- **File:** `packages/core/src/connectors/postgres/datasources/common/types.ts`
378
-
379
- | Type | Description |
380
- |------|-------------|
381
- | `TRelationalConnector<Schema>` | Canonical connector type - a Drizzle `PgDatabase` that **every** pg driver (`node-postgres`, `postgres-js`) satisfies. Use this in new code. |
382
- | `TNodePostgresConnector<Schema>` | **`@deprecated`** compat alias for `TRelationalConnector<Schema>` |
383
- | `TNodePostgresTransactionConnector<Schema>` | **`@deprecated`** compat alias - was the `PoolClient`-specific transaction connector; now aliases `TRelationalConnector<Schema>` |
384
- | `TAnyConnector<Schema>` | Alias of `TRelationalConnector<Schema>` |
385
- | `TAnyDataSourceSchema` | `Record<string, any>` - base type for all schema objects (defined in `src/base/datasources/common/types.ts`, shared across engines) |
386
-
387
- ### `DataSourceDrivers`
388
-
389
- Static class for driver validation (defined in `src/base/datasources/common/types.ts`, shared across engines):
390
-
391
- ```typescript
392
- DataSourceDrivers.NODE_POSTGRES // 'node-postgres'
393
- DataSourceDrivers.POSTGRES_JS // 'postgres-js'
394
- DataSourceDrivers.TYPESENSE // 'typesense'
395
- DataSourceDrivers.MEILISEARCH // 'meilisearch'
396
- DataSourceDrivers.isValid('node-postgres') // true
397
- ```
398
-
399
- > [!NOTE]
400
- > `NODE_POSTGRES`/`POSTGRES_JS` remain valid `TDataSourceDriver` string values, but `@datasource({ driver })` on a **relational** datasource no longer accepts them - it takes the `NodePostgresDriver`/`PostgresJsDriver` class instead (see [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers)). Search connectors (`TYPESENSE`, `MEILISEARCH`) still take the driver-name string form.
401
-
402
- ## Transaction Support
403
-
404
- Only engines that declare `getCapabilities().transactions === true` implement real transactions - currently just the PostgreSQL connector. Calling `beginTransaction()` on the typesense connector throws `NotSupported` (HTTP 501).
405
-
406
- ### How It Works
407
-
408
- `BasePostgresDataSource.beginTransaction()` does the following:
409
-
410
- 1. Resolves a driver from `this.client` (or the one `useDriver()` wired) and acquires a connection from it
411
- 2. Executes `BEGIN TRANSACTION ISOLATION LEVEL <level>` on the client
412
- 3. Creates a separate Drizzle connector scoped to that client
413
- 4. Returns an `IDatabaseTransaction` object with `commit()`, `rollback()`, and the scoped `connector`
414
-
415
- When `commit()` or `rollback()` succeeds, the client is released back to the pool.
416
-
417
- > [!WARNING] `commit()`/`rollback()` throw on failure
418
- > A failed `COMMIT` or `ROLLBACK` **throws** (a failed `COMMIT` no longer resolves as success), and the poisoned connection is **destroyed** rather than returned to the pool - under the `node-postgres` driver, which can discard a connection; `postgres-js` has no destroy semantics and pools it anyway. A failed `BEGIN` also destroys the acquired connection rather than leaking it. Because `rollback()` can throw and is normally called from a `catch`, nest it in its own `try...catch` so the rollback error does not replace the original cause. See [Transactions](/guides/core-concepts/persistent/transactions) and [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers).
419
-
420
- ### Neutral vs. PostgreSQL Transaction Types
421
-
422
- `src/base` declares the engine-neutral shape; the PostgreSQL connector narrows it with connection details.
423
-
424
- ```typescript
425
- // packages/core/src/base/datasources/common/types.ts - engine-neutral
426
- interface ITransaction<_Schema = unknown> {
427
- isActive: boolean;
428
- commit(): Promise<void>;
429
- rollback(): Promise<void>;
430
- }
431
-
432
- // packages/core/src/connectors/postgres/datasources/common/types.ts - PostgreSQL
433
- interface IDatabaseTransaction<Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema>
434
- extends ITransaction<Schema> {
435
- connector: TRelationalConnector<Schema>;
436
- isolationLevel: TIsolationLevel;
92
+ import { PostgresJsDriver } from '@venizia/ignis/postgres/postgres-js';
93
+ import postgres from 'postgres';
94
+ import type { Sql } from 'postgres';
95
+ import * as schema from '@/schemas';
96
+
97
+ @datasource({ driver: PostgresJsDriver })
98
+ export class PostgresDataSource extends BasePostgresDataSource<
99
+ IDataSourceConfigs,
100
+ typeof schema,
101
+ {},
102
+ Sql
103
+ > {
104
+ override configure(): void {
105
+ this.client = postgres(this.getConnectionString());
106
+ }
437
107
  }
438
108
  ```
439
109
 
440
- | Type | Description |
441
- |------|-------------|
442
- | `ITransaction<Schema>` | Engine-neutral contract - `isActive`, `commit()`, `rollback()`. No connector field. |
443
- | `IDatabaseTransaction<Schema>` | PostgreSQL transaction object - extends `ITransaction` with `connector` and `isolationLevel` |
444
- | `IDatabaseTransactionOptions` | Options for starting a PostgreSQL transaction (`isolationLevel`); extends the neutral `ITransactionOptions` |
445
- | `IDatabaseExtraOptions` | Extends the neutral `IExtraOptions`, narrowing `transaction?: IDatabaseTransaction` |
446
- | `TIsolationLevel` | Union type: `'READ COMMITTED'` \| `'REPEATABLE READ'` \| `'SERIALIZABLE'` |
447
- | `IsolationLevels` | Static class with isolation level constants and validation |
448
-
449
- > [!NOTE]
450
- > `AbstractRepository`, `PersistableRepository`, and every other engine-neutral repository type parameter is named `TOptions`/`IExtraOptions` in `src/base`. The PostgreSQL connector's `PostgresBaseRepository` narrows `ExtraOptions` to default to `IDatabaseExtraOptions`, so repository code written against a `PostgresBaseRepository` subclass sees `IDatabaseTransaction` (with `connector`/`isolationLevel`) rather than the bare neutral `ITransaction`.
110
+ See [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers) for the full comparison, client-validation behavior, and Supabase presets.
451
111
 
452
- ### Isolation Levels
112
+ ### Run a transaction
453
113
 
454
- Use the `IsolationLevels` static class for type-safe isolation level constants:
114
+ `beginTransaction()` acquires a connection, issues `BEGIN`, and returns a handle with a scoped `connector`, `commit()`, and `rollback()`.
455
115
 
456
116
  ```typescript
457
- import { IsolationLevels } from '@venizia/ignis';
117
+ import { IsolationLevels } from '@venizia/ignis/postgres';
118
+ import { userTable } from '@/schemas';
458
119
 
459
- // Available levels
460
- IsolationLevels.READ_COMMITTED // Default - prevents dirty reads
461
- IsolationLevels.REPEATABLE_READ // Consistent reads within transaction
462
- IsolationLevels.SERIALIZABLE // Strictest isolation
463
-
464
- // Validation
465
- IsolationLevels.isValid('READ COMMITTED'); // true
466
- IsolationLevels.isValid('INVALID'); // false
467
- ```
468
-
469
- > [!NOTE]
470
- > The default isolation level is `READ COMMITTED` when no `isolationLevel` option is provided.
471
-
472
- ### Usage Example
473
-
474
- ```typescript
475
- // Start transaction from datasource or repository
476
- const tx = await dataSource.beginTransaction({
477
- isolationLevel: IsolationLevels.SERIALIZABLE
120
+ const transaction = await postgresDataSource.beginTransaction({
121
+ isolationLevel: IsolationLevels.SERIALIZABLE,
478
122
  });
479
123
 
480
124
  try {
481
- // Use tx.connector for operations
482
- await tx.connector.insert(userTable).values({ name: 'Alice' });
483
- await tx.connector.insert(profileTable).values({ userId: '...', bio: 'Hello' });
484
-
485
- await tx.commit();
125
+ await transaction.connector.insert(userTable).values({ name: 'Alice' });
126
+ await transaction.commit();
486
127
  } catch (error) {
487
- // rollback() throws if ROLLBACK itself fails - nest it so it never replaces the original cause
488
128
  try {
489
- await tx.rollback();
129
+ await transaction.rollback();
490
130
  } catch (rollbackError) {
491
- logger.error('Rollback failed | %s', rollbackError);
131
+ console.error('Rollback failed | %s', rollbackError);
492
132
  }
493
133
  throw error;
494
134
  }
495
135
  ```
496
136
 
497
- > **Note:** For most use cases, prefer using `repository.beginTransaction()` which provides a higher-level API. See [Repositories Reference](./repositories/#transactions) for details.
137
+ - **`rollback()` throws on failure.** Nest it in its own `try...catch` so a rollback failure never replaces the original error.
138
+ - **Prefer the repository API.** Most repository code should call `repository.beginTransaction()` instead of going through the datasource directly. See [Transactions](/guides/core-concepts/persistent/transactions).
139
+
140
+ ### Share one datasource across repositories
498
141
 
499
- This architecture ensures that datasources are configured consistently and that the fully-initialized Drizzle connector, aware of all schemas and relations, is available to repositories for querying.
142
+ One `PostgresDataSource` instance is shared by every repository bound to it - `@repository` auto-injects the datasource, and `getSchema()` merges the tables and relations of every model bound to it.
500
143
 
501
- ## See Also
144
+ ```typescript
145
+ @repository({ model: User, dataSource: PostgresDataSource })
146
+ export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
147
+
148
+ @repository({ model: Configuration, dataSource: PostgresDataSource })
149
+ export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {}
150
+ ```
502
151
 
503
- - **Related Concepts:**
504
- - [Connectors](./connectors) - Base-vs-connectors architecture, dual-door exports, aliases
505
- - [DataSources Guide](/guides/core-concepts/persistent/datasources) - Creating DataSources tutorial
506
- - [Repositories](/guides/core-concepts/persistent/repositories) - Using DataSources for database access
507
- - [Models](/guides/core-concepts/persistent/models) - Entity schemas loaded by DataSource
508
- - [Transactions](/guides/core-concepts/persistent/transactions) - Multi-operation database transactions
509
- - [Search & Typesense](/guides/core-concepts/persistent/search-typesense) - The typesense connector
152
+ `PostgresDataSource.schema` automatically includes both `User` and `Configuration`, plus their relations - one pooled connection, no per-repository connection setup.
510
153
 
511
- - **References:**
512
- - [Repositories API](/references/base/repositories/) - Data access layer
513
- - [Environment Variables](/references/configuration/environment-variables) - Configuration management
154
+ ## See also
514
155
 
515
- - **External Resources:**
516
- - [Drizzle ORM Documentation](https://orm.drizzle.team/) - ORM configuration
517
- - [node-postgres Documentation](https://node-postgres.com/) - Connection pooling guide
156
+ - [Full reference](/references/base/datasources-reference) - every `IDataSource` member, `BasePostgresDataSource` internals, and transaction edge cases
157
+ - [Tutorial](/guides/core-concepts/persistent/datasources) - creating datasources step by step
158
+ - [Connectors](/references/base/connectors) - the base-vs-connector architecture
159
+ - [Repositories](/references/base/repositories/) - the layer that queries through a datasource
160
+ - [Models](/references/base/models) - the schema a datasource discovers from `@repository` bindings
518
161
 
519
- - **Best Practices:**
520
- - [Performance Optimization](/best-practices/performance-optimization) - Connection pool tuning
521
- - [Security Guidelines](/best-practices/security-guidelines) - Database credential management
162
+ **Files:**
522
163
 
523
- - **Tutorials:**
524
- - [Complete Installation](/guides/tutorials/complete-installation) - Database setup
525
- - [Building a CRUD API](/guides/tutorials/building-a-crud-api) - DataSource configuration
164
+ - [`packages/core/src/base/datasources/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/datasources/abstract.ts) - neutral `AbstractDataSource`
165
+ - [`packages/core/src/connectors/postgres/datasources/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/base.ts) - PostgreSQL `BasePostgresDataSource`