@venizia/ignis-docs 0.0.7-2 → 0.0.8-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 (161) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  6. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  8. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  9. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  11. package/package.json +1 -1
  12. package/wiki/best-practices/api-usage-examples.md +9 -9
  13. package/wiki/best-practices/architectural-patterns.md +19 -3
  14. package/wiki/best-practices/architecture-decisions.md +6 -6
  15. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  16. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  17. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  18. package/wiki/best-practices/code-style-standards/index.md +2 -2
  19. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  20. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  21. package/wiki/best-practices/data-modeling.md +1 -1
  22. package/wiki/best-practices/deployment-strategies.md +1 -1
  23. package/wiki/best-practices/error-handling.md +2 -2
  24. package/wiki/best-practices/performance-optimization.md +3 -3
  25. package/wiki/best-practices/security-guidelines.md +2 -2
  26. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  27. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  28. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  29. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  30. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  31. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  32. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  33. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  34. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  35. package/wiki/{references → extensions}/components/health-check.md +5 -4
  36. package/wiki/{references → extensions}/components/index.md +2 -0
  37. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  38. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  39. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  40. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  41. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  42. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  43. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  44. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  45. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  46. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  47. package/wiki/{references → extensions}/components/swagger.md +3 -3
  48. package/wiki/{references → extensions}/components/template/index.md +4 -4
  49. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  50. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  51. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  52. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  53. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  54. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  55. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  56. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  57. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  58. package/wiki/{references → extensions}/helpers/index.md +18 -6
  59. package/wiki/{references → extensions}/helpers/kafka/admin.md +33 -16
  60. package/wiki/extensions/helpers/kafka/consumer.md +384 -0
  61. package/wiki/extensions/helpers/kafka/examples.md +361 -0
  62. package/wiki/extensions/helpers/kafka/index.md +639 -0
  63. package/wiki/{references → extensions}/helpers/kafka/producer.md +100 -96
  64. package/wiki/extensions/helpers/kafka/schema-registry.md +214 -0
  65. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  66. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  67. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  68. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  69. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  70. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  71. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  72. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  73. package/wiki/extensions/index.md +48 -0
  74. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  75. package/wiki/guides/core-concepts/application/index.md +95 -35
  76. package/wiki/guides/core-concepts/components-guide.md +23 -19
  77. package/wiki/guides/core-concepts/components.md +34 -10
  78. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  79. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  80. package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
  81. package/wiki/guides/core-concepts/persistent/models.md +43 -1
  82. package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
  83. package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
  84. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
  85. package/wiki/guides/core-concepts/services.md +19 -5
  86. package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
  87. package/wiki/guides/get-started/philosophy.md +1 -1
  88. package/wiki/guides/index.md +2 -2
  89. package/wiki/guides/reference/glossary.md +7 -7
  90. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  91. package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
  92. package/wiki/guides/tutorials/complete-installation.md +17 -14
  93. package/wiki/guides/tutorials/ecommerce-api.md +18 -18
  94. package/wiki/guides/tutorials/realtime-chat.md +8 -8
  95. package/wiki/guides/tutorials/testing.md +2 -2
  96. package/wiki/index.md +4 -3
  97. package/wiki/references/base/application.md +341 -21
  98. package/wiki/references/base/bootstrapping.md +43 -13
  99. package/wiki/references/base/components.md +259 -8
  100. package/wiki/references/base/controllers.md +556 -253
  101. package/wiki/references/base/datasources.md +159 -79
  102. package/wiki/references/base/dependency-injection.md +299 -48
  103. package/wiki/references/base/filter-system/application-usage.md +18 -2
  104. package/wiki/references/base/filter-system/array-operators.md +14 -6
  105. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  106. package/wiki/references/base/filter-system/default-filter.md +28 -3
  107. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  108. package/wiki/references/base/filter-system/index.md +169 -11
  109. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  110. package/wiki/references/base/filter-system/list-operators.md +4 -3
  111. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  112. package/wiki/references/base/filter-system/null-operators.md +50 -0
  113. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  114. package/wiki/references/base/filter-system/range-operators.md +7 -1
  115. package/wiki/references/base/filter-system/tips.md +34 -7
  116. package/wiki/references/base/filter-system/use-cases.md +6 -5
  117. package/wiki/references/base/grpc-controllers.md +984 -0
  118. package/wiki/references/base/index.md +32 -24
  119. package/wiki/references/base/middleware.md +347 -0
  120. package/wiki/references/base/models.md +390 -46
  121. package/wiki/references/base/providers.md +14 -14
  122. package/wiki/references/base/repositories/advanced.md +84 -69
  123. package/wiki/references/base/repositories/index.md +447 -12
  124. package/wiki/references/base/repositories/mixins.md +103 -98
  125. package/wiki/references/base/repositories/relations.md +129 -45
  126. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  127. package/wiki/references/base/services.md +94 -14
  128. package/wiki/references/index.md +12 -10
  129. package/wiki/references/quick-reference.md +98 -65
  130. package/wiki/references/utilities/crypto.md +21 -4
  131. package/wiki/references/utilities/date.md +25 -7
  132. package/wiki/references/utilities/index.md +26 -24
  133. package/wiki/references/utilities/jsx.md +54 -54
  134. package/wiki/references/utilities/module.md +8 -6
  135. package/wiki/references/utilities/parse.md +16 -9
  136. package/wiki/references/utilities/performance.md +22 -7
  137. package/wiki/references/utilities/promise.md +19 -16
  138. package/wiki/references/utilities/request.md +48 -26
  139. package/wiki/references/utilities/schema.md +69 -6
  140. package/wiki/references/utilities/statuses.md +131 -140
  141. package/wiki/references/helpers/kafka/consumer.md +0 -473
  142. package/wiki/references/helpers/kafka/examples.md +0 -234
  143. package/wiki/references/helpers/kafka/index.md +0 -482
  144. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  145. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  146. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  147. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  148. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  149. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  150. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  151. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  152. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  153. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  154. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  155. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  156. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  157. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  158. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  159. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  160. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  161. /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
@@ -4,13 +4,13 @@ Dependency Injection (DI) enables loosely coupled, testable code by automaticall
4
4
 
5
5
  > **Deep Dive:** See [DI Reference](../../references/base/dependency-injection.md) for technical details on Container, Binding, and `@inject`.
6
6
 
7
- > **Standalone Package:** The core DI container is available as the standalone `@venizia/ignis-inversion` package for use outside the Ignis framework. See [Inversion Package Reference](/references/helpers/inversion/) for details.
7
+ > **Standalone Package:** The core DI container is available as the standalone `@venizia/ignis-inversion` package for use outside the Ignis framework. See [Inversion Package Reference](/extensions/helpers/inversion/) for details.
8
8
 
9
9
  ## Core Concepts
10
10
 
11
11
  | Concept | Description |
12
12
  | :--- | :--- |
13
- | **Container** | The central registry for all your application's services and dependencies. The `Application` class itself acts as the container. |
13
+ | **Container** | The central registry for all your application's services and dependencies. The `Application` class itself extends `Container`. |
14
14
  | **Binding** | The process of registering a class or value with the container under a specific key (e.g., `'services.UserService'`). |
15
15
  | **Injection**| The process of requesting a dependency from the container using the `@inject` decorator. |
16
16
 
@@ -42,6 +42,13 @@ graph TD
42
42
  end
43
43
  ```
44
44
 
45
+ ### Instantiation Algorithm (Two-Phase)
46
+
47
+ When the container instantiates a class, it follows a two-phase process:
48
+
49
+ 1. **Constructor injection** -- Reads `@inject` metadata from the class, sorts by parameter index, resolves each dependency from the container, and passes them as constructor arguments.
50
+ 2. **Property injection** -- After construction, reads property metadata and assigns each dependency to the decorated properties.
51
+
45
52
  ## Binding Dependencies
46
53
 
47
54
  Before a dependency can be injected, it must be **bound** to the container. This is typically done in the `preConfigure` method of your `Application` class.
@@ -50,13 +57,13 @@ Before a dependency can be injected, it must be **bound** to the container. This
50
57
 
51
58
  The `Application` class provides helper methods for common resource types. These automatically create a binding with a conventional key.
52
59
 
53
- | Method | Default Key |
54
- | :--- | :--- |
55
- | `app.service(UserService)` | `services.UserService` |
56
- | `app.repository(UserRepository)` | `repositories.UserRepository` |
57
- | `app.dataSource(PostgresDataSource)` | `datasources.PostgresDataSource` |
58
- | `app.controller(UserController)` | `controllers.UserController` |
59
- | `app.component(MyComponent)` | `components.MyComponent` |
60
+ | Method | Default Key | Default Scope |
61
+ | :--- | :--- | :--- |
62
+ | `app.service(UserService)` | `services.UserService` | Transient |
63
+ | `app.repository(UserRepository)` | `repositories.UserRepository` | Transient |
64
+ | `app.dataSource(PostgresDataSource)` | `datasources.PostgresDataSource` | **Singleton** |
65
+ | `app.controller(UserController)` | `controllers.UserController` | Transient |
66
+ | `app.component(MyComponent)` | `components.MyComponent` | **Singleton** |
60
67
 
61
68
  All these methods accept an optional second parameter to customize the binding key:
62
69
 
@@ -85,15 +92,29 @@ this.bind<string>({ key: 'API_KEY' }).toValue('my-secret-api-key');
85
92
 
86
93
  You can control the lifecycle of your dependencies with scopes.
87
94
 
88
- - **`TRANSIENT`** (default): A new instance is created every time the dependency is injected.
89
- - **`SINGLETON`**: A single instance is created once and reused for all subsequent injections.
95
+ - **`TRANSIENT`** (default): A new instance is created every time the dependency is resolved.
96
+ - **`SINGLETON`**: A single instance is created once and cached. All subsequent resolutions return the same instance.
90
97
 
91
98
  ```typescript
99
+ import { BindingScopes } from '@venizia/ignis-inversion';
100
+
92
101
  this.bind({ key: 'services.MySingletonService' })
93
102
  .toClass(MySingletonService)
94
103
  .setScope(BindingScopes.SINGLETON); // Use SINGLETON for this service
95
104
  ```
96
105
 
106
+ ### Binding Tags
107
+
108
+ Bindings are automatically tagged with their namespace prefix. For example, a binding with key `'services.UserService'` is auto-tagged with `'services'`. You can also add custom tags:
109
+
110
+ ```typescript
111
+ this.bind({ key: 'services.UserService' })
112
+ .toClass(UserService)
113
+ .setTags('critical', 'user-domain');
114
+ ```
115
+
116
+ Tags are used by the container's `findByTag()` method to discover bindings by category.
117
+
97
118
  ## Injecting Dependencies
98
119
 
99
120
  `Ignis` provides the `@inject` decorator to request dependencies from the container.
@@ -103,16 +124,16 @@ this.bind({ key: 'services.MySingletonService' })
103
124
  This makes dependencies explicit and ensures they are available right away.
104
125
 
105
126
  ```typescript
106
- import { BaseController, controller, inject } from '@venizia/ignis';
127
+ import { BaseRestController, controller, inject } from '@venizia/ignis';
107
128
  import { UserService } from '../services/user.service';
108
129
 
109
130
  @controller({ path: '/users' })
110
- export class UserController extends BaseController {
131
+ export class UserController extends BaseRestController {
111
132
  constructor(
112
133
  @inject({ key: 'services.UserService' })
113
134
  private userService: UserService
114
135
  ) {
115
- super({ scope: UserController.name, path: '/users' });
136
+ super({ scope: UserController.name });
116
137
  }
117
138
 
118
139
  // ... you can now use this.userService
@@ -130,34 +151,56 @@ import { UserService } from '../services/user.service';
130
151
  export class UserComponent {
131
152
  @inject({ key: 'services.UserService' })
132
153
  private userService: UserService;
133
-
154
+
134
155
  // ...
135
156
  }
136
157
  ```
137
158
 
159
+ ### Optional Dependencies
160
+
161
+ Mark a dependency as optional to avoid errors when it's not bound:
162
+
163
+ ```typescript
164
+ constructor(
165
+ @inject({ key: 'services.CacheService', isOptional: true })
166
+ private cache?: CacheService
167
+ ) {
168
+ super({ scope: MyService.name });
169
+ // cache will be undefined if not bound
170
+ }
171
+ ```
172
+
138
173
  ## Providers
139
174
 
140
- Providers are used for dependencies that require complex setup logic. A provider is a class that implements a `value()` method, which is responsible for creating and returning the dependency instance.
175
+ Providers are used for dependencies that require complex setup logic. A provider can be either a factory function or a class that implements the `IProvider<T>` interface with a `value()` method.
141
176
 
142
- ### Creating a Custom Provider
177
+ ### Factory Function Provider
143
178
 
144
179
  ```typescript
145
- import { BaseProvider, inject, Container, IConfig } from '@venizia/ignis';
146
- import { ThirdPartyApiClient } from '../services/api-client.service';
147
-
148
- // Assume IConfig is a configuration object we've bound elsewhere
149
- @injectable() // Make the provider itself injectable
150
- export class ApiClientProvider extends BaseProvider<ThirdPartyApiClient> {
151
- @inject({ key: 'configs.api' })
152
- private apiConfig: IConfig;
153
-
154
- // The container calls this method to get the instance
180
+ // In your application class
181
+ this.bind<ThirdPartyApiClient>({ key: 'services.ApiClient' })
182
+ .toProvider((container) => {
183
+ const config = container.get<IConfig>({ key: 'configs.api' });
184
+ return new ThirdPartyApiClient({
185
+ apiKey: config.apiKey,
186
+ baseUrl: config.baseUrl,
187
+ });
188
+ });
189
+ ```
190
+
191
+ ### Class-Based Provider
192
+
193
+ ```typescript
194
+ import { IProvider, Container } from '@venizia/ignis-inversion';
195
+
196
+ export class ApiClientProvider implements IProvider<ThirdPartyApiClient> {
155
197
  value(container: Container): ThirdPartyApiClient {
198
+ const config = container.get<IConfig>({ key: 'configs.api' });
156
199
  const client = new ThirdPartyApiClient({
157
- apiKey: this.apiConfig.apiKey,
158
- baseUrl: this.apiConfig.baseUrl,
200
+ apiKey: config.apiKey,
201
+ baseUrl: config.baseUrl,
159
202
  });
160
- client.connect(); // Perform initial setup
203
+ client.connect();
161
204
  return client;
162
205
  }
163
206
  }
@@ -192,7 +235,15 @@ container.bind({ key: 'services.Cache' })
192
235
 
193
236
  // Resolve dependencies
194
237
  const logger = container.get<LoggerService>({ key: 'services.Logger' });
195
- const apiKey = container.getSync<string>({ key: 'config.apiKey' });
238
+ const apiKey = container.get<string>({ key: 'config.apiKey' });
239
+
240
+ // Resolve multiple at once
241
+ const [svcA, svcB] = container.gets<[ServiceA, ServiceB]>({
242
+ bindings: [
243
+ { key: 'services.A' },
244
+ { key: 'services.B', isOptional: true },
245
+ ],
246
+ });
196
247
  ```
197
248
 
198
249
  ### Use Cases
@@ -245,19 +296,33 @@ describe('UserService', () => {
245
296
  | **Use Case** | Main application | Testing, isolated modules, workers |
246
297
 
247
298
  > [!TIP]
248
- > The `Application` class extends `Container`, so all container methods (`bind`, `get`, `getSync`) are available on your application instance. Standalone containers are useful when you need isolation from the main application context.
299
+ > The `Application` class extends `Container`, so all container methods (`bind`, `get`, `gets`, `resolve`, `findByTag`, `isBound`, `unbind`) are available on your application instance. Standalone containers are useful when you need isolation from the main application context.
300
+
301
+ ### Container API Summary
302
+
303
+ | Method | Description |
304
+ | :--- | :--- |
305
+ | `bind<T>({ key })` | Create a new binding |
306
+ | `get<T>({ key, isOptional? })` | Resolve a single dependency |
307
+ | `gets<T>({ bindings })` | Resolve multiple dependencies at once |
308
+ | `resolve<T>(cls)` | Instantiate a class with DI (alias for `instantiate`) |
309
+ | `isBound({ key })` | Check if a key is bound |
310
+ | `unbind({ key })` | Remove a binding |
311
+ | `findByTag({ tag, exclude? })` | Find bindings by tag |
312
+ | `clear()` | Clear all cached singleton instances |
313
+ | `reset()` | Remove all bindings entirely |
249
314
 
250
315
  ## See Also
251
316
 
252
317
  - **Related Concepts:**
253
318
  - [Application](/guides/core-concepts/application/) - Application extends Container
254
- - [Controllers](/guides/core-concepts/controllers) - Use DI for injecting services
319
+ - [Controllers](/guides/core-concepts/rest-controllers) - Use DI for injecting services
255
320
  - [Services](/guides/core-concepts/services) - Use DI for injecting repositories
256
321
  - [Providers](/references/base/providers) - Factory pattern for dynamic injection
257
322
 
258
323
  - **References:**
259
324
  - [Dependency Injection API](/references/base/dependency-injection) - Complete API reference
260
- - [Inversion Helper](/references/helpers/inversion/) - DI container utilities
325
+ - [Inversion Helper](/extensions/helpers/inversion/) - DI container utilities
261
326
  - [Glossary](/guides/reference/glossary#dependency-injection-di) - DI concepts explained
262
327
 
263
328
  - **Tutorials:**
@@ -0,0 +1,295 @@
1
+ # gRPC Controllers
2
+
3
+ Ignis provides first-class support for gRPC via the [ConnectRPC](https://connectrpc.com/) protocol. gRPC controllers use Protobuf service definitions for strongly-typed RPC methods, and are served over the same Hono HTTP server as REST controllers.
4
+
5
+ > **Deep Dive:** See [gRPC Controllers Reference](../../references/base/grpc-controllers.md) for the complete API.
6
+
7
+ > [!IMPORTANT]
8
+ > The current version only supports **unary** RPCs (single request, single response) over HTTP/1.1 Connect protocol. Streaming methods (`@serverStream`, `@clientStream`, `@bidiStream`) have decorators defined for metadata registration, but will throw an error at runtime if used.
9
+
10
+ ## Peer Dependencies
11
+
12
+ gRPC support requires the following packages to be installed:
13
+
14
+ ```bash
15
+ bun add @connectrpc/connect @bufbuild/protobuf
16
+ ```
17
+
18
+ ## Enabling gRPC Transport
19
+
20
+ To use gRPC controllers, you must enable the gRPC transport in your application configuration:
21
+
22
+ ```typescript
23
+ import { ControllerTransports } from '@venizia/ignis';
24
+
25
+ export const appConfigs: IApplicationConfigs = {
26
+ // ... other config
27
+ transports: [ControllerTransports.REST, ControllerTransports.GRPC],
28
+ };
29
+ ```
30
+
31
+ If `transports` is not specified, only `REST` is enabled by default. If gRPC controllers are discovered but the gRPC transport is not enabled, the framework will log an error warning.
32
+
33
+ ## Creating a gRPC Controller
34
+
35
+ Extend `BaseGrpcController` and use the `@controller` decorator with `transport: ControllerTransports.GRPC` and a `service` reference to your generated Protobuf service definition.
36
+
37
+ ```typescript
38
+ import { create } from '@bufbuild/protobuf';
39
+ import {
40
+ BaseGrpcController,
41
+ ControllerTransports,
42
+ controller,
43
+ inject,
44
+ unary,
45
+ TRouteContext,
46
+ } from '@venizia/ignis';
47
+ import {
48
+ GreeterService,
49
+ SayHelloResponseSchema,
50
+ type SayHelloRequest,
51
+ type SayHelloResponse,
52
+ } from './generated/greeter_pb';
53
+
54
+ @controller({
55
+ path: '/grpc',
56
+ transport: ControllerTransports.GRPC,
57
+ service: GreeterService,
58
+ })
59
+ export class GreeterController extends BaseGrpcController {
60
+ constructor(
61
+ @inject({ key: 'services.GreeterService' })
62
+ private readonly greeterService: GreeterService,
63
+ ) {
64
+ super({ scope: 'GreeterController' });
65
+ }
66
+
67
+ override binding() {}
68
+
69
+ @unary({ configs: { name: 'sayHello' } })
70
+ async sayHello(opts: { request: SayHelloRequest; context: TRouteContext }): Promise<SayHelloResponse> {
71
+ const message = await this.greeterService.greet({ name: opts.request.name });
72
+ return create(SayHelloResponseSchema, { message });
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### Key Points
78
+
79
+ - The `service` field in `@controller` must reference the generated ConnectRPC service definition (e.g., `GreeterService`)
80
+ - The `path` determines the URL prefix where the gRPC service is mounted
81
+ - `binding()` must be implemented (even if empty) -- it is called during `configure()`
82
+ - Handler methods receive `{ request, context }` and return a Protobuf message object
83
+
84
+ ## RPC Method Decorators
85
+
86
+ Ignis provides a decorator for each gRPC method type:
87
+
88
+ - `@unary(opts)` -- Single request, single response. **This is the only supported method type** in the current version.
89
+ - `@serverStream(opts)` -- Decorator exists for metadata, but throws at runtime.
90
+ - `@clientStream(opts)` -- Decorator exists for metadata, but throws at runtime.
91
+ - `@bidiStream(opts)` -- Decorator exists for metadata, but throws at runtime.
92
+ - `@rpc(opts)` -- Generic RPC decorator where you specify the `method` in the configs.
93
+
94
+ The `opts` object contains a `configs` property with at minimum a `name` field that matches the RPC method name in your Protobuf service definition. You can also specify `authenticate` and `authorize` options, just like REST routes.
95
+
96
+ ```typescript
97
+ // Unary (supported)
98
+ @unary({ configs: { name: 'sayHello' } })
99
+ async sayHello(opts: { request: SayHelloRequest; context: TRouteContext }): Promise<SayHelloResponse> { ... }
100
+
101
+ // With authentication
102
+ @unary({
103
+ configs: {
104
+ name: 'getUser',
105
+ authenticate: { strategies: ['jwt'], mode: 'required' },
106
+ },
107
+ })
108
+ async getUser(opts: { request: GetUserRequest; context: TRouteContext }): Promise<GetUserResponse> { ... }
109
+
110
+ // With authorization
111
+ @unary({
112
+ configs: {
113
+ name: 'deleteUser',
114
+ authenticate: { strategies: ['jwt'] },
115
+ authorize: { resource: 'user', scopes: ['delete'] },
116
+ },
117
+ })
118
+ async deleteUser(opts: { request: DeleteUserRequest; context: TRouteContext }): Promise<DeleteUserResponse> { ... }
119
+ ```
120
+
121
+ ## Transport Configuration
122
+
123
+ The transport type is set via the `transport` field in the `@controller` decorator:
124
+
125
+ ```typescript
126
+ // REST controller (default -- transport can be omitted)
127
+ @controller({ path: '/users' })
128
+
129
+ // gRPC controller
130
+ @controller({ path: '/grpc', transport: ControllerTransports.GRPC, service: MyService })
131
+ ```
132
+
133
+ The `ControllerTransports` class provides the available transport constants:
134
+
135
+ - `ControllerTransports.REST` -- Default HTTP/JSON transport
136
+ - `ControllerTransports.GRPC` -- ConnectRPC transport
137
+
138
+ ## Manual Route Definition
139
+
140
+ Just like REST controllers, gRPC controllers support manual route definition via `defineRoute` and `bindRoute`:
141
+
142
+ ```typescript
143
+ import { GRPC } from '@venizia/ignis-helpers';
144
+
145
+ override binding() {
146
+ // Using defineRoute
147
+ this.defineRoute({
148
+ configs: { name: 'sayHello', method: GRPC.Methods.UNARY },
149
+ handler: async (opts) => {
150
+ return create(SayHelloResponseSchema, { message: `Hello ${opts.request.name}` });
151
+ },
152
+ });
153
+
154
+ // Using bindRoute (fluent API)
155
+ this.bindRoute({
156
+ configs: { name: 'getUser', method: GRPC.Methods.UNARY },
157
+ }).to({
158
+ handler: async (opts) => {
159
+ return create(GetUserResponseSchema, { name: 'John' });
160
+ },
161
+ });
162
+ }
163
+ ```
164
+
165
+ ## Minimal Controller (No DI)
166
+
167
+ For simple services that don't need injected dependencies:
168
+
169
+ ```typescript
170
+ import { create } from '@bufbuild/protobuf';
171
+ import { BaseGrpcController, ControllerTransports, controller, unary } from '@venizia/ignis';
172
+ import { EchoService, EchoResponseSchema, type EchoRequest, type EchoResponse } from './generated/echo_pb';
173
+
174
+ @controller({
175
+ path: '/echo',
176
+ transport: ControllerTransports.GRPC,
177
+ service: EchoService,
178
+ })
179
+ export class EchoController extends BaseGrpcController {
180
+ constructor() {
181
+ super({ scope: 'EchoController' });
182
+ }
183
+
184
+ override binding() {}
185
+
186
+ @unary({ configs: { name: 'echo' } })
187
+ async echo(opts: { request: EchoRequest }): Promise<EchoResponse> {
188
+ return create(EchoResponseSchema, { message: opts.request.message });
189
+ }
190
+ }
191
+ ```
192
+
193
+ ## Key Differences from REST Controllers
194
+
195
+ | Aspect | REST Controller | gRPC Controller |
196
+ | :--- | :--- | :--- |
197
+ | **Base class** | `BaseRestController` | `BaseGrpcController` |
198
+ | **Router** | `OpenAPIHono` | `Hono` (plain) |
199
+ | **Route identifier** | `path` + HTTP method | `name` (Protobuf method name) |
200
+ | **Handler signature** | `(c: TRouteContext) => Response` | `(opts: { request, context }) => ResponseMessage` |
201
+ | **Response format** | `c.json()` calls | Protobuf message objects via `create()` |
202
+ | **Schema** | Zod + OpenAPI | Protobuf (`@bufbuild/protobuf`) |
203
+ | **`binding()` method** | Optional (can use decorators only) | Must be implemented (even if empty) |
204
+ | **`configure()` return** | `OpenAPIHono` router | `void` (adapter mounted internally) |
205
+ | **Peer dependencies** | None | `@connectrpc/connect`, `@bufbuild/protobuf` |
206
+
207
+ ## Protobuf Setup
208
+
209
+ gRPC controllers require Protobuf service definitions. The typical workflow:
210
+
211
+ ### 1. Define your `.proto` file
212
+
213
+ ```protobuf
214
+ // proto/greeter.proto
215
+ syntax = "proto3";
216
+
217
+ package greeter;
218
+
219
+ service GreeterService {
220
+ rpc SayHello (SayHelloRequest) returns (SayHelloResponse);
221
+ rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
222
+ }
223
+
224
+ message SayHelloRequest {
225
+ string name = 1;
226
+ }
227
+
228
+ message SayHelloResponse {
229
+ string message = 1;
230
+ }
231
+
232
+ message ListUsersRequest {}
233
+
234
+ message ListUsersResponse {
235
+ repeated User users = 1;
236
+ }
237
+
238
+ message User {
239
+ string id = 1;
240
+ string name = 2;
241
+ string email = 3;
242
+ }
243
+ ```
244
+
245
+ ### 2. Generate TypeScript code
246
+
247
+ Use [Buf](https://buf.build/) to generate TypeScript from your `.proto` files:
248
+
249
+ ```yaml
250
+ # buf.gen.yaml
251
+ version: v2
252
+ plugins:
253
+ - remote: buf.build/bufbuild/es
254
+ out: src/controllers/greeter/generated
255
+ ```
256
+
257
+ ```bash
258
+ npx buf generate src/controllers/greeter/proto
259
+ ```
260
+
261
+ ### 3. Use the generated types in your controller
262
+
263
+ The generated `*_pb.ts` files export service definitions, message schemas, and TypeScript types that you use directly in your controller (as shown in the examples above).
264
+
265
+ ## Client Usage
266
+
267
+ You can test gRPC controllers using the ConnectRPC client:
268
+
269
+ ```typescript
270
+ import { createClient } from '@connectrpc/connect';
271
+ import { createConnectTransport } from '@connectrpc/connect-web';
272
+ import { GreeterService } from './controllers/greeter/generated/greeter_pb';
273
+
274
+ const transport = createConnectTransport({
275
+ baseUrl: 'http://localhost:3000',
276
+ });
277
+
278
+ const client = createClient(GreeterService, transport);
279
+
280
+ const response = await client.sayHello({ name: 'World' });
281
+ console.log(response.message); // "Hello, World!"
282
+ ```
283
+
284
+ ## See Also
285
+
286
+ - **Related Concepts:**
287
+ - [Application](/guides/core-concepts/application/) - Registering controllers and enabling transports
288
+ - [REST Controllers](/guides/core-concepts/rest-controllers) - HTTP/JSON controllers
289
+ - [Services](/guides/core-concepts/services) - Business logic layer called by controllers
290
+ - [Dependency Injection](/guides/core-concepts/dependency-injection) - Injecting services into controllers
291
+
292
+ - **References:**
293
+ - [BaseGrpcController API](/references/base/grpc-controllers) - Complete gRPC controller API reference
294
+ - [Authentication](/extensions/components/authentication/) - Securing gRPC endpoints
295
+ - [Authorization](/extensions/components/authorization/) - Role-based access control for RPCs
@@ -27,7 +27,7 @@ interface IDSConfigs {
27
27
  }
28
28
 
29
29
  @datasource({ driver: 'node-postgres' })
30
- export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
30
+ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
31
31
  constructor() {
32
32
  super({
33
33
  name: PostgresDataSource.name,
@@ -64,9 +64,11 @@ export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, I
64
64
 
65
65
  **How auto-discovery works:**
66
66
 
67
- 1. `@repository` decorators register model-datasource bindings
68
- 2. When `configure()` is called, `getSchema()` collects all bound models
69
- 3. Drizzle is initialized with the complete schema
67
+ 1. `@repository` decorators register model-datasource bindings in the `MetadataRegistry`
68
+ 2. When `configure()` is called, `getSchema()` invokes `discoverSchema()` which calls `MetadataRegistry.buildSchema({ dataSource })` to collect all bound models and their relations
69
+ 3. Drizzle is initialized with the complete schema (tables + Drizzle relations)
70
+
71
+ You can disable auto-discovery per datasource via `@datasource({ driver: 'node-postgres', autoDiscovery: false })`.
70
72
 
71
73
  ## Manual Schema (Optional)
72
74
 
@@ -74,7 +76,7 @@ If you need explicit control, you can still provide schema manually:
74
76
 
75
77
  ```typescript
76
78
  @datasource({ driver: 'node-postgres' })
77
- export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
79
+ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
78
80
  constructor() {
79
81
  super({
80
82
  name: PostgresDataSource.name,
@@ -89,6 +91,21 @@ export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, I
89
91
  }
90
92
  ```
91
93
 
94
+ ## DataSource Hierarchy
95
+
96
+ ```
97
+ AbstractDataSource extends BaseHelper
98
+ └── BaseDataSource
99
+ ├── configure() # Setup pool + Drizzle connector (abstract)
100
+ ├── getConnectionString() # Build connection URL (abstract)
101
+ ├── getSchema() # Auto-discover from @repository bindings
102
+ ├── discoverSchema() # Internal: reads MetadataRegistry
103
+ ├── hasDiscoverableModels() # Check if any repos reference this DS
104
+ ├── beginTransaction(opts?) # Start transaction with isolation level
105
+ ├── getConnector() # Get Drizzle connector
106
+ └── getSettings() # Get connection config
107
+ ```
108
+
92
109
  ## Registering a DataSource
93
110
 
94
111
  ```typescript
@@ -100,18 +117,20 @@ export class Application extends BaseApplication {
100
117
  }
101
118
  ```
102
119
 
120
+ DataSources are bound as **singletons** to ensure connection pool sharing across the application.
121
+
103
122
  ## Supported Drivers
104
123
 
105
124
  | Driver | Package | Status |
106
125
  |--------|---------|--------|
107
- | `node-postgres` | `pg` | Supported |
126
+ | `node-postgres` | `pg` | Supported |
108
127
  | `mysql2` | `mysql2` | Planned |
109
128
  | `better-sqlite3` | `better-sqlite3` | Planned |
110
129
 
111
130
  ## DataSource Template
112
131
 
113
132
  ```typescript
114
- import { BaseDataSource, datasource, TNodePostgresConnector, ValueOrPromise } from '@venizia/ignis';
133
+ import { BaseDataSource, datasource, ValueOrPromise } from '@venizia/ignis';
115
134
  import { drizzle } from 'drizzle-orm/node-postgres';
116
135
  import { Pool } from 'pg';
117
136
 
@@ -124,7 +143,7 @@ interface IDSConfigs {
124
143
  }
125
144
 
126
145
  @datasource({ driver: 'node-postgres' })
127
- export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
146
+ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
128
147
  constructor() {
129
148
  super({
130
149
  name: PostgresDataSource.name,
@@ -208,6 +208,35 @@ const [fullUser] = await connector
208
208
  For complete hidden properties documentation, see the [Models Reference](../../../references/base/models.md#hidden-properties).
209
209
  :::
210
210
 
211
+ ## Default Filter
212
+
213
+ Apply automatic filters to all repository queries. This is commonly used for soft-delete patterns:
214
+
215
+ ```typescript
216
+ @model({
217
+ type: 'entity',
218
+ settings: {
219
+ defaultFilter: { where: { isDeleted: false } },
220
+ hiddenProperties: ['deletedAt'],
221
+ },
222
+ })
223
+ export class Article extends BaseEntity<typeof Article.schema> {
224
+ // ...
225
+ }
226
+ ```
227
+
228
+ The default filter is applied automatically to all read operations. Bypass it with `shouldSkipDefaultFilter: true` in the options:
229
+
230
+ ```typescript
231
+ // Normal query - auto-filters out soft-deleted records
232
+ const articles = await articleRepo.find({});
233
+
234
+ // Include deleted records
235
+ const allArticles = await articleRepo.find({
236
+ options: { shouldSkipDefaultFilter: true },
237
+ });
238
+ ```
239
+
211
240
  ## Authorization Settings
212
241
 
213
242
  Declare your model's authorization principal directly in `@model` settings. The decorator auto-populates `AUTHORIZATION_SUBJECT` for type-safe references in route configs:
@@ -237,9 +266,22 @@ authorize: {
237
266
  ```
238
267
 
239
268
  :::tip
240
- For full authorization integration details, see the [Authorization Usage Reference](../../../references/components/authorization/usage#model-based-resource-references).
269
+ For full authorization integration details, see the [Authorization Usage Reference](../../../extensions/components/authorization/usage#model-based-resource-references).
241
270
  :::
242
271
 
272
+ ## Model Metadata Types
273
+
274
+ The `@model` decorator accepts the following metadata:
275
+
276
+ | Field | Type | Description |
277
+ | :--- | :--- | :--- |
278
+ | `type` | `'entity' \| 'view'` | Whether this is a table or a database view |
279
+ | `tableName` | `string` | Optional explicit table name |
280
+ | `skipMigrate` | `boolean` | Skip this model during migrations |
281
+ | `settings.hiddenProperties` | `string[]` | Properties excluded from all query results |
282
+ | `settings.defaultFilter` | `TFilter` | Default filter auto-applied to all queries |
283
+ | `settings.authorize.principal` | `string` | Authorization subject name for this model |
284
+
243
285
  ## Model Template
244
286
 
245
287
  ```typescript