@venizia/ignis-docs 0.0.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/mcp-server/dist/common/config.d.ts +27 -0
  2. package/mcp-server/dist/common/config.d.ts.map +1 -0
  3. package/mcp-server/dist/common/config.js +27 -0
  4. package/mcp-server/dist/common/config.js.map +1 -0
  5. package/mcp-server/dist/common/index.d.ts +3 -0
  6. package/mcp-server/dist/common/index.d.ts.map +1 -0
  7. package/mcp-server/dist/common/index.js +19 -0
  8. package/mcp-server/dist/common/index.js.map +1 -0
  9. package/mcp-server/dist/common/paths.d.ts +13 -0
  10. package/mcp-server/dist/common/paths.d.ts.map +1 -0
  11. package/mcp-server/dist/common/paths.js +23 -0
  12. package/mcp-server/dist/common/paths.js.map +1 -0
  13. package/mcp-server/dist/helpers/docs.helper.d.ts +81 -0
  14. package/mcp-server/dist/helpers/docs.helper.d.ts.map +1 -0
  15. package/mcp-server/dist/helpers/docs.helper.js +171 -0
  16. package/mcp-server/dist/helpers/docs.helper.js.map +1 -0
  17. package/mcp-server/dist/helpers/index.d.ts +3 -0
  18. package/mcp-server/dist/helpers/index.d.ts.map +1 -0
  19. package/mcp-server/dist/helpers/index.js +19 -0
  20. package/mcp-server/dist/helpers/index.js.map +1 -0
  21. package/mcp-server/dist/helpers/logger.helper.d.ts +7 -0
  22. package/mcp-server/dist/helpers/logger.helper.d.ts.map +1 -0
  23. package/mcp-server/dist/helpers/logger.helper.js +22 -0
  24. package/mcp-server/dist/helpers/logger.helper.js.map +1 -0
  25. package/mcp-server/dist/index.d.ts +3 -0
  26. package/mcp-server/dist/index.d.ts.map +1 -0
  27. package/mcp-server/dist/index.js +62 -0
  28. package/mcp-server/dist/index.js.map +1 -0
  29. package/mcp-server/dist/tools/base.tool.d.ts +98 -0
  30. package/mcp-server/dist/tools/base.tool.d.ts.map +1 -0
  31. package/mcp-server/dist/tools/base.tool.js +47 -0
  32. package/mcp-server/dist/tools/base.tool.js.map +1 -0
  33. package/mcp-server/dist/tools/get-doc-content.tool.d.ts +30 -0
  34. package/mcp-server/dist/tools/get-doc-content.tool.d.ts.map +1 -0
  35. package/mcp-server/dist/tools/get-doc-content.tool.js +127 -0
  36. package/mcp-server/dist/tools/get-doc-content.tool.js.map +1 -0
  37. package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts +40 -0
  38. package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts.map +1 -0
  39. package/mcp-server/dist/tools/get-doc-metadata.tool.js +121 -0
  40. package/mcp-server/dist/tools/get-doc-metadata.tool.js.map +1 -0
  41. package/mcp-server/dist/tools/index.d.ts +8 -0
  42. package/mcp-server/dist/tools/index.d.ts.map +1 -0
  43. package/mcp-server/dist/tools/index.js +18 -0
  44. package/mcp-server/dist/tools/index.js.map +1 -0
  45. package/mcp-server/dist/tools/list-categories.tool.d.ts +20 -0
  46. package/mcp-server/dist/tools/list-categories.tool.d.ts.map +1 -0
  47. package/mcp-server/dist/tools/list-categories.tool.js +105 -0
  48. package/mcp-server/dist/tools/list-categories.tool.js.map +1 -0
  49. package/mcp-server/dist/tools/list-docs.tool.d.ts +32 -0
  50. package/mcp-server/dist/tools/list-docs.tool.d.ts.map +1 -0
  51. package/mcp-server/dist/tools/list-docs.tool.js +121 -0
  52. package/mcp-server/dist/tools/list-docs.tool.js.map +1 -0
  53. package/mcp-server/dist/tools/search-docs.tool.d.ts +32 -0
  54. package/mcp-server/dist/tools/search-docs.tool.d.ts.map +1 -0
  55. package/mcp-server/dist/tools/search-docs.tool.js +120 -0
  56. package/mcp-server/dist/tools/search-docs.tool.js.map +1 -0
  57. package/package.json +102 -0
  58. package/wiki/get-started/5-minute-quickstart.md +266 -0
  59. package/wiki/get-started/best-practices/api-usage-examples.md +222 -0
  60. package/wiki/get-started/best-practices/architectural-patterns.md +129 -0
  61. package/wiki/get-started/best-practices/code-style-standards.md +122 -0
  62. package/wiki/get-started/best-practices/common-pitfalls.md +136 -0
  63. package/wiki/get-started/best-practices/contribution-workflow.md +145 -0
  64. package/wiki/get-started/best-practices/deployment-strategies.md +121 -0
  65. package/wiki/get-started/best-practices/performance-optimization.md +88 -0
  66. package/wiki/get-started/best-practices/security-guidelines.md +97 -0
  67. package/wiki/get-started/best-practices/troubleshooting-tips.md +100 -0
  68. package/wiki/get-started/building-a-crud-api.md +717 -0
  69. package/wiki/get-started/core-concepts/application.md +168 -0
  70. package/wiki/get-started/core-concepts/components.md +96 -0
  71. package/wiki/get-started/core-concepts/controllers.md +441 -0
  72. package/wiki/get-started/core-concepts/dependency-injection.md +160 -0
  73. package/wiki/get-started/core-concepts/persistent.md +591 -0
  74. package/wiki/get-started/core-concepts/services.md +88 -0
  75. package/wiki/get-started/index.md +65 -0
  76. package/wiki/get-started/mcp-docs-server.md +840 -0
  77. package/wiki/get-started/philosophy.md +123 -0
  78. package/wiki/get-started/prerequisites.md +113 -0
  79. package/wiki/get-started/quickstart.md +382 -0
  80. package/wiki/index.md +48 -0
  81. package/wiki/references/base/application.md +67 -0
  82. package/wiki/references/base/components.md +80 -0
  83. package/wiki/references/base/controllers.md +361 -0
  84. package/wiki/references/base/datasources.md +105 -0
  85. package/wiki/references/base/dependency-injection.md +83 -0
  86. package/wiki/references/base/models.md +104 -0
  87. package/wiki/references/base/repositories.md +118 -0
  88. package/wiki/references/base/services.md +97 -0
  89. package/wiki/references/components/authentication.md +224 -0
  90. package/wiki/references/components/health-check.md +190 -0
  91. package/wiki/references/components/index.md +61 -0
  92. package/wiki/references/components/request-tracker.md +35 -0
  93. package/wiki/references/components/socket-io.md +127 -0
  94. package/wiki/references/components/swagger.md +175 -0
  95. package/wiki/references/helpers/cron.md +94 -0
  96. package/wiki/references/helpers/crypto.md +117 -0
  97. package/wiki/references/helpers/env.md +67 -0
  98. package/wiki/references/helpers/error.md +80 -0
  99. package/wiki/references/helpers/index.md +21 -0
  100. package/wiki/references/helpers/inversion.md +141 -0
  101. package/wiki/references/helpers/logger.md +98 -0
  102. package/wiki/references/helpers/network.md +143 -0
  103. package/wiki/references/helpers/queue.md +131 -0
  104. package/wiki/references/helpers/redis.md +121 -0
  105. package/wiki/references/helpers/socket-io.md +103 -0
  106. package/wiki/references/helpers/storage.md +130 -0
  107. package/wiki/references/helpers/testing.md +115 -0
  108. package/wiki/references/helpers/worker-thread.md +162 -0
  109. package/wiki/references/src-details/core.md +249 -0
  110. package/wiki/references/src-details/dev-configs.md +302 -0
  111. package/wiki/references/src-details/docs.md +61 -0
  112. package/wiki/references/src-details/helpers.md +211 -0
  113. package/wiki/references/src-details/inversion.md +345 -0
  114. package/wiki/references/src-details/mcp-server.md +726 -0
  115. package/wiki/references/utilities/crypto.md +39 -0
  116. package/wiki/references/utilities/date.md +72 -0
  117. package/wiki/references/utilities/index.md +12 -0
  118. package/wiki/references/utilities/module.md +40 -0
  119. package/wiki/references/utilities/parse.md +68 -0
  120. package/wiki/references/utilities/performance.md +64 -0
  121. package/wiki/references/utilities/promise.md +83 -0
  122. package/wiki/references/utilities/request.md +66 -0
  123. package/wiki/references/utilities/schema.md +88 -0
@@ -0,0 +1,441 @@
1
+ # Controllers
2
+
3
+ Controllers handle HTTP requests and return responses - they're your API endpoints.
4
+
5
+ > **Deep Dive:** See [Controllers Reference](../../references/base/controllers.md) for advanced patterns.
6
+
7
+ ## Creating a Controller
8
+
9
+ Extend `BaseController` and use decorators to define routes:
10
+
11
+ ```typescript
12
+ import { BaseController, controller, get, jsonResponse, z } from '@venizia/ignis';
13
+ import { Context } from 'hono';
14
+
15
+ @controller({ path: '/users' })
16
+ export class UserController extends BaseController {
17
+ constructor() {
18
+ // It's good practice to pass a scope for logging
19
+ super({ scope: UserController.name, path: '/users' });
20
+ }
21
+
22
+ @get({
23
+ configs: {
24
+ path: '/',
25
+ responses: jsonResponse({
26
+ description: 'A list of users',
27
+ schema: z.array(z.object({ id: z.string(), name: z.string() })),
28
+ }),
29
+ },
30
+ })
31
+ getAllUsers(c: Context) {
32
+ return c.json([{ id: '1', name: 'John Doe' }]);
33
+ }
34
+ }
35
+ ```
36
+ Notice that the `binding()` method is no longer needed when using decorators.
37
+
38
+ ## Controller Lifecycle
39
+
40
+ Controllers have a simple and predictable lifecycle managed by the application.
41
+
42
+ | Stage | Method | Description |
43
+ | :--- | :--- | :--- |
44
+ | **1. Instantiation** | `constructor(opts)` | The controller is created by the DI container. Dependencies are injected, and you call `super()` to initialize the internal Hono router. |
45
+ | **2. Configuration**| `registerControllers` phase | The application automatically discovers and registers all routes defined with decorators (`@get`, `@post`, etc.) on your controller methods. If you have routes defined manually inside `binding()`, that method is also called during this phase. **Note:** If you exclusively use decorators for routing, the `binding()` method can be omitted from your controller. |
46
+
47
+ ## Defining Routes with Decorators (Recommended)
48
+
49
+ The recommended way to define routes is by using decorators directly on the controller methods that handle them. This approach is more declarative, keeps your code organized, and provides **full type safety** for your request parameters, query, body, and even the response.
50
+
51
+ :::tip Type Safety without Boilerplate
52
+ For decorator-based routes, you do not need to explicitly annotate the return type with `TRouteResponse`. TypeScript will automatically infer and validate the return type against the OpenAPI response schema you define in your `configs`. This gives you full type safety with less code.
53
+ :::
54
+
55
+ ### HTTP Method Decorators
56
+
57
+ `Ignis` provides a decorator for each common HTTP method:
58
+
59
+ - `@get(opts)`
60
+ - `@post(opts)`
61
+ - `@put(opts)`
62
+ - `@patch(opts)`
63
+ - `@del(opts)`
64
+ - `@api(opts)` (a generic decorator where you specify the method in the `configs`)
65
+
66
+ The `opts` object contains a `configs` property that defines the route's path, request validation, and OpenAPI response schemas.
67
+
68
+ **Example using decorators with `ROUTE_CONFIGS` and full type inference:**
69
+
70
+ For optimal organization and type safety, define your route configurations in a constant with `as const`. This allows TypeScript to precisely infer the types for your request data and expected responses within your handler methods.
71
+
72
+ ```typescript
73
+ import { BaseController, controller, get, post, HTTP, jsonContent, jsonResponse, TRouteContext } from '@venizia/ignis';
74
+ import { z } from '@hono/zod-openapi';
75
+
76
+ // Define route configs as const for type inference
77
+ const TEST_ROUTES = {
78
+ getData: {
79
+ method: HTTP.Methods.GET,
80
+ path: '/',
81
+ responses: jsonResponse({
82
+ description: 'A simple message',
83
+ schema: z.object({ message: z.string(), method: z.string() }),
84
+ }),
85
+ },
86
+ createItem: {
87
+ method: HTTP.Methods.POST,
88
+ path: '/',
89
+ request: {
90
+ body: jsonContent({
91
+ description: 'Request body for creating an item',
92
+ schema: z.object({ name: z.string(), value: z.number().int().positive() }),
93
+ }),
94
+ },
95
+ responses: jsonResponse({
96
+ description: 'Created item',
97
+ schema: z.object({ id: z.string(), name: z.string(), value: z.number() }),
98
+ }),
99
+ },
100
+ } as const; // Crucial for strict type inference!
101
+
102
+ @controller({ path: '/my-items' })
103
+ export class MyItemsController extends BaseController {
104
+ constructor() {
105
+ super({ scope: MyItemsController.name, path: '/my-items' });
106
+ }
107
+
108
+ @get({ configs: TEST_ROUTES.getData })
109
+ getData(c: TRouteContext<typeof TEST_ROUTES.getData>) { // Return type is automatically inferred
110
+ // 'c' is fully typed here, including c.req.valid and c.json return type
111
+ return c.json({ message: 'Hello from decorator', method: 'GET' }, HTTP.ResultCodes.RS_2.Ok);
112
+ }
113
+
114
+ @post({ configs: TEST_ROUTES.createItem })
115
+ createItem(c: TRouteContext<typeof TEST_ROUTES.createItem>) { // Return type is automatically inferred
116
+ // c.req.valid('json') is automatically typed based on createItem.request.body.content['application/json'].schema
117
+ const body = c.req.valid('json');
118
+
119
+ // Return type is automatically validated against createItem.responses[200].content['application/json'].schema
120
+ return c.json(
121
+ {
122
+ id: 'some-uuid',
123
+ name: body.name,
124
+ value: body.value,
125
+ },
126
+ HTTP.ResultCodes.RS_2.Ok,
127
+ );
128
+ }
129
+ }
130
+ ```
131
+
132
+ ## Manual Route Definition: An Alternative Approach
133
+
134
+ While decorators are the recommended approach for most use cases, `Ignis` also provides a manual way to define routes within the controller's `binding()` method.
135
+
136
+ ### Decorator vs Manual: Quick Comparison
137
+
138
+ | Aspect | Decorators (`@get`, `@post`) | Manual (`defineRoute`, `bindRoute`) |
139
+ | :--- | :--- | :--- |
140
+ | **Syntax** | Clean, declarative | More verbose |
141
+ | **Organization** | Route config near the handler | Can be in separate files |
142
+ | **Use Case** | Most standard CRUD endpoints | Dynamic route generation |
143
+ | **Type Safety** | Full inference with `TRouteContext` | Full inference with `TRouteContext` |
144
+ | **Readability** | High - easy to scan | Medium - requires scrolling |
145
+ | **Best For** | 90% of use cases | Advanced scenarios |
146
+
147
+ ### When to Use Manual Route Definition
148
+
149
+ Manual route definition is useful for:
150
+
151
+ - **Dynamically generating routes** based on configuration (e.g., loading routes from a database)
152
+ - **Organizing all routes** for a controller within a single method if you prefer centralized route declarations
153
+ - **Conditional route registration** (e.g., enabling/disabling routes based on feature flags)
154
+ - **Developers who prefer non-decorator syntax** (coming from Express/Fastify)
155
+
156
+ When using this method, you will override the `binding()` method in your controller and use `this.defineRoute` or `this.bindRoute` to register your endpoints.
157
+
158
+ ### `defineRoute`
159
+
160
+ Use this method for defining a single API endpoint with all its configurations and handler. It also benefits from type inference when used with `TRouteContext`.
161
+
162
+ ```typescript
163
+ import { Authentication, HTTP, jsonResponse, z, TRouteContext } from '@venizia/ignis';
164
+
165
+ // ... inside the binding() method
166
+
167
+ const GetUsersRoute = {
168
+ path: '/',
169
+ method: 'get',
170
+ authStrategies: [Authentication.STRATEGY_JWT],
171
+ responses: jsonResponse({
172
+ description: 'List of all users',
173
+ schema: z.array(z.object({ id: z.number(), name: z.string() })),
174
+ }),
175
+ } as const;
176
+
177
+ this.defineRoute({
178
+ configs: GetUsersRoute,
179
+ handler: (c: TRouteContext<typeof GetUsersRoute>) => { // Return type is automatically inferred
180
+ return c.json([{ id: 1, name: 'John Doe' }]);
181
+ },
182
+ });
183
+ ```
184
+
185
+ ### `bindRoute`
186
+
187
+ This method offers a fluent API for defining routes, similar to `defineRoute`, but structured for chaining. It also benefits from `TRouteContext` for type safety.
188
+
189
+ ```typescript
190
+ import { jsonResponse, z, TRouteContext } from '@venizia/ignis';
191
+
192
+ // ... inside the binding() method
193
+
194
+ const GetUserByIdRoute = {
195
+ path: '/:id',
196
+ method: 'get',
197
+ responses: jsonResponse({
198
+ description: 'A single user',
199
+ schema: z.object({ id: z.string(), name: z.string() }),
200
+ }),
201
+ } as const;
202
+
203
+ this.bindRoute({
204
+ configs: GetUserByIdRoute,
205
+ }).to({
206
+ handler: (c: TRouteContext<typeof GetUserByIdRoute>) => { // Return type is automatically inferred
207
+ const { id } = c.req.param();
208
+ return c.json({ id: id, name: 'John Doe' });
209
+ },
210
+ });
211
+ ```
212
+
213
+ ## `ControllerFactory` for CRUD Operations
214
+
215
+ For standard CRUD (Create, Read, Update, Delete) operations, `Ignis` provides a `ControllerFactory` that can generate a full-featured controller for any given entity. This significantly reduces boilerplate code.
216
+
217
+ ```typescript
218
+ // src/controllers/configuration.controller.ts (Example from @examples/vert)
219
+ import { Configuration } from '@/models';
220
+ import { ConfigurationRepository } from '@/repositories';
221
+ import {
222
+ BindingKeys,
223
+ BindingNamespaces,
224
+ controller,
225
+ ControllerFactory,
226
+ inject,
227
+ } from '@venizia/ignis';
228
+
229
+ const BASE_PATH = '/configurations';
230
+
231
+ const _Controller = ControllerFactory.defineCrudController({
232
+ repository: { name: ConfigurationRepository.name },
233
+ controller: {
234
+ name: 'ConfigurationController',
235
+ basePath: BASE_PATH,
236
+ isStrict: true,
237
+ },
238
+ entity: () => Configuration, // Provide a resolver for your entity class
239
+ });
240
+
241
+ @controller({ path: BASE_PATH })
242
+ export class ConfigurationController extends _Controller {
243
+ constructor(
244
+ @inject({
245
+ key: BindingKeys.build({
246
+ namespace: BindingNamespaces.REPOSITORY,
247
+ key: ConfigurationRepository.name,
248
+ }),
249
+ })
250
+ repository: ConfigurationRepository,
251
+ ) {
252
+ super(repository); // The generated controller expects the repository in its constructor
253
+ }
254
+ }
255
+ ```
256
+ The `ControllerFactory.defineCrudController` method automatically sets up the following routes based on your entity schema:
257
+
258
+ | Name | Method | Path | Description |
259
+ | :--- | :--- | :--- | :--- |
260
+ | `count` | `GET` | `/count` | Get the number of records matching a filter. |
261
+ | `find` | `GET` | `/` | Retrieve all records matching a filter. |
262
+ | `findById` | `GET` | `/:id` | Retrieve a single record by its ID. |
263
+ | `findOne` | `GET` | `/find-one` | Retrieve a single record matching a filter. |
264
+ | `create` | `POST` | `/` | Create a new record. |
265
+ | `updateById` | `PATCH` | `/:id` | Update a record by its ID. |
266
+ | `updateAll` | `PATCH` | `/` | Update multiple records matching a filter. |
267
+ | `deleteById` | `DELETE` | `/:id` | Delete a record by its ID. |
268
+ | `deleteAll` | `DELETE` | `/` | Delete multiple records matching a filter. |
269
+
270
+ :::info Customization
271
+ The `ControllerFactory` is highly customizable. You can override the Zod schemas for any of the generated routes to add, remove, or modify fields for request validation and response shapes. You can also configure other behaviors, like making delete operations return the deleted records.
272
+
273
+ For a full list of customization options, see the [**Deep Dive: `ControllerFactory`**](../../references/base/controllers.md#controllerfactory) documentation.
274
+ :::
275
+
276
+ ### `defineJSXRoute` (Server-Side Rendered HTML)
277
+
278
+ Use this method for routes that render HTML pages using JSX components. This is perfect for building server-side rendered web applications.
279
+
280
+ ```typescript
281
+ import { htmlResponse } from '@venizia/ignis';
282
+
283
+ // ... inside the binding() method
284
+
285
+ this.defineJSXRoute({
286
+ configs: {
287
+ path: '/',
288
+ method: 'get',
289
+ description: 'Home page',
290
+ responses: htmlResponse({
291
+ description: 'HTML home page',
292
+ }),
293
+ },
294
+ handler: (c) => {
295
+ const user = { name: 'John Doe' };
296
+ return c.html(<HomePage user={user} />);
297
+ },
298
+ });
299
+ ```
300
+
301
+ **Key Points:**
302
+ - The handler **must** return `c.html()` with a JSX component
303
+ - JSX routes automatically include HTML content-type in OpenAPI documentation
304
+ - Views are typically organized in `src/views/` directory
305
+ - Components can be reused across multiple pages
306
+
307
+ **Example View Component:**
308
+
309
+ ```typescript
310
+ // src/views/pages/home.page.tsx
311
+ import type { FC } from '@venizia/ignis';
312
+ import { MainLayout } from '../layouts/main.layout';
313
+
314
+ interface HomePageProps {
315
+ user: { name: string };
316
+ }
317
+
318
+ export const HomePage: FC<HomePageProps> = ({ user }) => {
319
+ return (
320
+ <MainLayout title="Home">
321
+ <h1>Welcome, {user.name}!</h1>
322
+ <p>This page is rendered on the server using JSX.</p>
323
+ </MainLayout>
324
+ );
325
+ };
326
+ ```
327
+
328
+ **Example Layout Component:**
329
+
330
+ ```typescript
331
+ // src/views/layouts/main.layout.tsx
332
+ import type { FC, PropsWithChildren } from '@venizia/ignis';
333
+
334
+ interface MainLayoutProps {
335
+ title: string;
336
+ }
337
+
338
+ export const MainLayout: FC<PropsWithChildren<MainLayoutProps>> = ({ title, children }) => {
339
+ return (
340
+ <html lang="en">
341
+ <head>
342
+ <meta charSet="UTF-8" />
343
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
344
+ <title>{title}</title>
345
+ </head>
346
+ <body>
347
+ <header>
348
+ <nav>
349
+ <a href="/">Home</a>
350
+ <a href="/about">About</a>
351
+ </nav>
352
+ </header>
353
+ <main>{children}</main>
354
+ <footer>
355
+ <p>© 2025 My App</p>
356
+ </footer>
357
+ </body>
358
+ </html>
359
+ );
360
+ };
361
+ ```
362
+
363
+ > **Note:** JSX support in `Ignis` uses Hono's built-in JSX runtime. Make sure your `tsconfig.json` includes the JSX configuration (this is already set up in the framework's base configuration).
364
+
365
+ ## Accessing Validated Request Data
366
+
367
+ When you define Zod schemas in your route's `request` configuration (whether with decorators or manual definition), Hono's validation middleware automatically parses and validates the incoming data. You can access this validated data using `c.req.valid()`.
368
+
369
+ ```typescript
370
+ import { z } from '@hono/zod-openapi';
371
+ import { jsonContent, put } from '@venizia/ignis';
372
+
373
+ // ... inside a controller class
374
+
375
+ const UserSchema = z.object({ name: z.string(), email: z.string().email() });
376
+
377
+ @put({
378
+ configs: {
379
+ path: '/:id',
380
+ method: 'put',
381
+ request: {
382
+ params: z.object({ id: z.string() }),
383
+ query: z.object({ notify: z.string().optional() }),
384
+ body: jsonContent({ schema: UserSchema }),
385
+ },
386
+ // ... responses
387
+ },
388
+ })
389
+ updateUser(c: Context) {
390
+ // Access validated data from the request
391
+ const { id } = c.req.valid('param');
392
+ const { notify } = c.req.valid('query');
393
+ const userUpdateData = c.req.valid('json'); // for body
394
+
395
+ console.log(`Updating user ${id} with data:`, userUpdateData);
396
+ if (notify) {
397
+ console.log('Notification is enabled.');
398
+ }
399
+
400
+ return c.json({ success: true, id, ...userUpdateData });
401
+ }
402
+ ```
403
+
404
+ Using `c.req.valid()` is the recommended way to access request data as it ensures that the data conforms to the schema you've defined. For even better type safety, you can use the `TRouteContext` utility type.
405
+
406
+ ```typescript
407
+ import { z } from '@hono/zod-openapi';
408
+ import { jsonContent, put, TRouteContext } from '@venizia/ignis';
409
+
410
+ // ... inside a controller class
411
+
412
+ const updateUserConfig = {
413
+ path: '/:id',
414
+ method: 'put',
415
+ request: {
416
+ params: z.object({ id: z.string() }),
417
+ query: z.object({ notify: z.string().optional() }),
418
+ body: jsonContent({
419
+ schema: z.object({ name: z.string(), email: z.string().email() }),
420
+ }),
421
+ },
422
+ // ... responses
423
+ } as const; // Use 'as const' for strict type inference
424
+
425
+ @put({ configs: updateUserConfig })
426
+ updateUser(c: TRouteContext<typeof updateUserConfig>) {
427
+ // Access validated data from the request
428
+ const { id } = c.req.valid('param');
429
+ const { notify } = c.req.valid('query');
430
+ const userUpdateData = c.req.valid('json'); // for body
431
+
432
+ console.log(`Updating user ${id} with data:`, userUpdateData);
433
+ if (notify) {
434
+ console.log('Notification is enabled.');
435
+ }
436
+
437
+ return c.json({ success: true, id, ...userUpdateData });
438
+ }
439
+ ```
440
+
441
+ Using `TRouteContext` provides full type inference for `c.req.valid()`, so your editor will know that `id` is a string, `notify` is an optional string, and `userUpdateData` matches the body schema.
@@ -0,0 +1,160 @@
1
+ # Dependency Injection
2
+
3
+ Dependency Injection (DI) enables loosely coupled, testable code by automatically providing dependencies to classes.
4
+
5
+ > **Deep Dive:** See [DI Reference](../../references/base/dependency-injection.md) for technical details on Container, Binding, and `@inject`.
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/src-details/inversion.md) for details.
8
+
9
+ ## Core Concepts
10
+
11
+ | Concept | Description |
12
+ | :--- | :--- |
13
+ | **Container** | The central registry for all your application's services and dependencies. The `Application` class itself acts as the container. |
14
+ | **Binding** | The process of registering a class or value with the container under a specific key (e.g., `'services.UserService'`). |
15
+ | **Injection**| The process of requesting a dependency from the container using the `@inject` decorator. |
16
+
17
+ ### How It Works: The DI Flow
18
+
19
+ ```mermaid
20
+ graph TD
21
+ A[1. Bind Resource] -- app.service(MyService) --> B(Container stores a Binding for 'services.MyService');
22
+ B -- triggers --> C[2. Instantiate Consumer];
23
+ C -- @inject({ key: 'services.MyService' }) --> D(Container reads injection metadata);
24
+ D -- finds binding --> E[3. Resolve & Inject];
25
+ E -- gets dependency --> F(Container creates/gets instance of MyService);
26
+ F -- injects into --> G(MyController instance is created with MyService);
27
+
28
+ subgraph Application Startup
29
+ A
30
+ end
31
+ subgraph On-Demand Instantiation
32
+ C
33
+ end
34
+ subgraph Container Internals
35
+ B
36
+ D
37
+ E
38
+ F
39
+ end
40
+ subgraph Result
41
+ G
42
+ end
43
+ ```
44
+
45
+ ## Binding Dependencies
46
+
47
+ 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.
48
+
49
+ ### Standard Resource Binding
50
+
51
+ The `Application` class provides helper methods for common resource types. These automatically create a binding with a conventional key.
52
+
53
+ | Method | Example 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
+
61
+ ### Custom Bindings
62
+
63
+ For other values or more complex setups, use the `bind` method directly.
64
+
65
+ ```typescript
66
+ // In your application class's preConfigure()
67
+ this.bind<MyCustomClass>({ key: 'MyCustomClass' }).toClass(MyCustomClass);
68
+
69
+ this.bind<string>({ key: 'API_KEY' }).toValue('my-secret-api-key');
70
+ ```
71
+
72
+ ### Binding Scopes
73
+
74
+ You can control the lifecycle of your dependencies with scopes.
75
+
76
+ - **`TRANSIENT`** (default): A new instance is created every time the dependency is injected.
77
+ - **`SINGLETON`**: A single instance is created once and reused for all subsequent injections.
78
+
79
+ ```typescript
80
+ this.bind({ key: 'services.MySingletonService' })
81
+ .toClass(MySingletonService)
82
+ .setScope(BindingScopes.SINGLETON); // Use SINGLETON for this service
83
+ ```
84
+
85
+ ## Injecting Dependencies
86
+
87
+ `Ignis` provides the `@inject` decorator to request dependencies from the container.
88
+
89
+ ### Constructor Injection (Recommended)
90
+
91
+ This makes dependencies explicit and ensures they are available right away.
92
+
93
+ ```typescript
94
+ import { BaseController, controller, inject } from '@venizia/ignis';
95
+ import { UserService } from '../services/user.service';
96
+
97
+ @controller({ path: '/users' })
98
+ export class UserController extends BaseController {
99
+ constructor(
100
+ @inject({ key: 'services.UserService' })
101
+ private userService: UserService
102
+ ) {
103
+ super({ scope: UserController.name, path: '/users' });
104
+ }
105
+
106
+ // ... you can now use this.userService
107
+ }
108
+ ```
109
+
110
+ ### Property Injection
111
+
112
+ You can also inject dependencies directly as class properties.
113
+
114
+ ```typescript
115
+ import { inject } from '@venizia/ignis';
116
+ import { UserService } from '../services/user.service';
117
+
118
+ export class UserComponent {
119
+ @inject({ key: 'services.UserService' })
120
+ private userService: UserService;
121
+
122
+ // ...
123
+ }
124
+ ```
125
+
126
+ ## Providers
127
+
128
+ 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.
129
+
130
+ ### Creating a Custom Provider
131
+
132
+ ```typescript
133
+ import { BaseProvider, inject, Container, IConfig } from '@venizia/ignis';
134
+ import { ThirdPartyApiClient } from '../services/api-client.service';
135
+
136
+ // Assume IConfig is a configuration object we've bound elsewhere
137
+ @injectable() // Make the provider itself injectable
138
+ export class ApiClientProvider extends BaseProvider<ThirdPartyApiClient> {
139
+ @inject({ key: 'configs.api' })
140
+ private apiConfig: IConfig;
141
+
142
+ // The container calls this method to get the instance
143
+ value(container: Container): ThirdPartyApiClient {
144
+ const client = new ThirdPartyApiClient({
145
+ apiKey: this.apiConfig.apiKey,
146
+ baseUrl: this.apiConfig.baseUrl,
147
+ });
148
+ client.connect(); // Perform initial setup
149
+ return client;
150
+ }
151
+ }
152
+ ```
153
+
154
+ You would then bind this provider in your application:
155
+
156
+ ```typescript
157
+ // In your application class
158
+ this.bind<ThirdPartyApiClient>({ key: 'services.ApiClient' })
159
+ .toProvider(ApiClientProvider);
160
+ ```