@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
@@ -9,13 +9,13 @@ difficulty: advanced
9
9
  Technical reference for the DI system in IGNIS - managing resource lifecycles and dependency resolution.
10
10
 
11
11
  **Files:**
12
- - `packages/inversion/src/container.ts` - Base `Container` and `Binding` classes
13
- - `packages/inversion/src/registry.ts` - Base `MetadataRegistry`
14
- - `packages/inversion/src/metadata/injectors.ts` - Base `@inject` and `@injectable` decorators
12
+ - `packages/inversion/src/modules/container/index.ts` - Base `Container` and `Binding` classes
13
+ - `packages/inversion/src/modules/registry/index.ts` - Base `MetadataRegistry`
14
+ - `packages/inversion/src/modules/metadata/injectors.ts` - Base `@inject` decorator
15
15
  - `packages/inversion/src/common/types.ts` - `BindingScopes`, `BindingValueTypes`, `BindingKeys`, `IProvider`
16
16
  - `packages/core/src/helpers/inversion/container.ts` - Extended `Container` with `ApplicationLogger`
17
17
  - `packages/core/src/helpers/inversion/registry.ts` - Extended `MetadataRegistry` (singleton, with model/repository/datasource mixins)
18
- - `packages/core/src/base/metadata/injectors.ts` - Core `@inject` and `@injectable` (wired to extended registry)
18
+ - `packages/core/src/base/metadata/injectors.ts` - Core `@inject` (wired to extended registry)
19
19
 
20
20
  ## Quick Reference
21
21
 
@@ -24,7 +24,6 @@ Technical reference for the DI system in IGNIS - managing resource lifecycles an
24
24
  | **Container** | DI registry managing resource lifecycles | `bind()`, `get()`, `gets()`, `instantiate()`, `resolve()`, `findByTag()`, `isBound()`, `unbind()`, `clear()`, `reset()` |
25
25
  | **Binding** | Single registered dependency configuration | `toClass()`, `toValue()`, `toProvider()`, `setScope()`, `setTags()`, `getValue()`, `clearCache()` |
26
26
  | **@inject** | Decorator marking injection points | Applied to constructor parameters and class properties |
27
- | **@injectable** | Decorator marking a class as injectable | Stores scope and tag metadata |
28
27
  | **MetadataRegistry** | Stores decorator metadata | Singleton - base via `metadataRegistry` export, core via `MetadataRegistry.getInstance()` |
29
28
  | **BindingKeys** | Utility for building namespaced keys | `BindingKeys.build({ namespace, key })` |
30
29
  | **Boot System** | Automatic artifact discovery and binding | Integrates with Container via tags and bindings |
@@ -42,7 +41,7 @@ Before reading this document, you should understand:
42
41
 
43
42
  Heart of the DI system - registry managing all application resources.
44
43
 
45
- **File:** `packages/inversion/src/container.ts` (Base) & `packages/core/src/helpers/inversion/container.ts` (Extended)
44
+ **File:** `packages/inversion/src/modules/container/index.ts` (Base) & `packages/core/src/helpers/inversion/container.ts` (Extended)
46
45
 
47
46
  The base `Container` extends `BaseHelper` (which provides `scope` and `identifier` properties). The core `Container` extends the base and adds a `Logger` instance.
48
47
 
@@ -106,7 +105,7 @@ class UserController {
106
105
 
107
106
  A `Binding` represents a single registered dependency in the container. It provides a fluent API to configure *how* a dependency should be created and managed.
108
107
 
109
- **File:** `packages/inversion/src/container.ts`
108
+ **File:** `packages/inversion/src/modules/container/index.ts`
110
109
 
111
110
  The `Binding` class extends `BaseHelper`.
112
111
 
@@ -206,7 +205,7 @@ This is also used internally by `container.get()` and `container.getBinding()` w
206
205
 
207
206
  The `@inject` decorator marks where dependencies should be injected - either on constructor parameters or class properties.
208
207
 
209
- **File:** `packages/inversion/src/metadata/injectors.ts` (base) & `packages/core/src/base/metadata/injectors.ts` (core wrapper)
208
+ **File:** `packages/inversion/src/modules/metadata/injectors.ts` (base) & `packages/core/src/base/metadata/injectors.ts` (core wrapper)
210
209
 
211
210
  ### Signature
212
211
 
@@ -257,30 +256,14 @@ The `@venizia/ignis-inversion` package exports base decorators that use the modu
257
256
 
258
257
  **Always import from `@venizia/ignis` in application code:**
259
258
  ```typescript
260
- import { inject, injectable } from '@venizia/ignis';
259
+ import { inject } from '@venizia/ignis';
261
260
  ```
262
261
 
263
- ## `@injectable` Decorator
262
+ ## Registering a Class
264
263
 
265
- Marks a class as injectable and attaches optional metadata.
266
-
267
- **File:** `packages/inversion/src/metadata/injectors.ts` (base) & `packages/core/src/base/metadata/injectors.ts` (core wrapper)
268
-
269
- ### Signature
270
-
271
- ```typescript
272
- @injectable({ scope?: TBindingScope; tags?: Record<string, any> })
273
- ```
274
-
275
- | Parameter | Type | Default | Description |
276
- | :--- | :--- | :--- | :--- |
277
- | `scope` | `'singleton' \| 'transient'` | - | Optional scope hint for the binding. |
278
- | `tags` | `Record<string, any>` | - | Optional metadata tags. |
279
-
280
- ### Example
264
+ No class decorator is needed to make a class injectable. A class becomes resolvable once a binding exists for it - created either by boot auto-discovery, by a framework helper (`app.controller()`, `app.service()`, `@repository`), or explicitly with `container.bind()`. Scope is configured on the binding, never on the class:
281
265
 
282
266
  ```typescript
283
- @injectable({ scope: BindingScopes.SINGLETON })
284
267
  class UserService extends BaseService {
285
268
  constructor(
286
269
  @inject({ key: 'repositories.UserRepository' })
@@ -289,15 +272,19 @@ class UserService extends BaseService {
289
272
  super({ scope: UserService.name });
290
273
  }
291
274
  }
275
+
276
+ app.bind({ key: 'services.UserService' })
277
+ .toClass(UserService)
278
+ .setScope(BindingScopes.SINGLETON); // default is TRANSIENT
292
279
  ```
293
280
 
294
281
  ## `MetadataRegistry`
295
282
 
296
- The `MetadataRegistry` stores and retrieves all metadata attached by decorators (`@inject`, `@injectable`, `@controller`, `@model`, etc.).
283
+ The `MetadataRegistry` stores and retrieves all metadata attached by decorators (`@inject`, `@controller`, `@model`, etc.).
297
284
 
298
285
  ### Base MetadataRegistry
299
286
 
300
- **File:** `packages/inversion/src/registry.ts`
287
+ **File:** `packages/inversion/src/modules/registry/index.ts`
301
288
 
302
289
  A singleton exported as `metadataRegistry`. Extends `BaseHelper`.
303
290
 
@@ -315,8 +302,6 @@ A singleton exported as `metadataRegistry`. Extends `BaseHelper`.
315
302
  | `setPropertyMetadata({ target, propertyName, metadata })` | Stores property injection metadata (`IPropertyMetadata`). |
316
303
  | `getPropertiesMetadata({ target })` | Returns a `Map<string \| symbol, IPropertyMetadata>` for all injected properties. |
317
304
  | `getPropertyMetadata({ target, propertyName })` | Returns property metadata for a specific property. |
318
- | `setInjectableMetadata({ target, metadata })` | Stores `@injectable` metadata on a class. |
319
- | `getInjectableMetadata({ target })` | Returns `@injectable` metadata for a class. |
320
305
 
321
306
  ### Core MetadataRegistry
322
307
 
@@ -333,12 +318,11 @@ Additional capabilities include:
333
318
 
334
319
  ### Metadata Keys
335
320
 
336
- Defined in `packages/inversion/src/common/keys.ts`:
321
+ Defined in `packages/inversion/src/modules/metadata/common/constants.ts`:
337
322
 
338
323
  ```typescript
339
324
  MetadataKeys.PROPERTIES = Symbol.for('ignis:properties')
340
325
  MetadataKeys.INJECT = Symbol.for('ignis:inject')
341
- MetadataKeys.INJECTABLE = Symbol.for('ignis:injectable')
342
326
  ```
343
327
 
344
328
  ### Key Types
@@ -355,11 +339,6 @@ interface IPropertyMetadata {
355
339
  isOptional?: boolean;
356
340
  [key: string]: any;
357
341
  }
358
-
359
- interface IInjectableMetadata {
360
- scope?: TBindingScope;
361
- tags?: Record<string, any>;
362
- }
363
342
  ```
364
343
 
365
344
  ## Boot System Integration
@@ -436,7 +415,6 @@ The boot system integrates into the application lifecycle:
436
415
  app.bind({ key: 'controllers.UserController' }).toClass(UserController);
437
416
 
438
417
  // 4. Later, when UserController is instantiated:
439
- @injectable()
440
418
  class UserController {
441
419
  constructor(
442
420
  @inject({ key: 'services.UserService' })
@@ -6,48 +6,27 @@ difficulty: intermediate
6
6
 
7
7
  # Using Filters in Your Application
8
8
 
9
- How filters flow through the application layers.
10
-
11
-
12
- ## Architecture Overview
9
+ A `filter` typically starts as a JSON-encoded query string on an HTTP request and ends as a Drizzle query - here is what happens at each layer in between.
13
10
 
14
11
  ```
15
- +-----------------------------------------------------------------+
16
- | HTTP Request |
17
- | GET /products?filter={"where":{"status":"active"},"limit":10} |
18
- +--------------------------------+--------------------------------+
19
- |
20
- v
21
- +-----------------------------------------------------------------+
22
- | Controller Layer |
23
- | - Validates filter via Zod schema |
24
- | - Parses JSON string -> Filter object |
25
- | - Passes to service/repository |
26
- +-----------------------------------------------------------------+
27
- |
28
- v
29
- +-----------------------------------------------------------------+
30
- | Service Layer (Optional) |
31
- | - Business logic, authorization |
32
- | - May modify filter before passing |
33
- +-----------------------------------------------------------------+
34
- |
35
- v
36
- +-----------------------------------------------------------------+
37
- | Repository Layer |
38
- | - applyDefaultFilter() merges the @model default filter |
39
- | - FilterBuilder transforms Filter -> Drizzle query options |
40
- | - Executes query via Drizzle ORM |
41
- | - Returns typed results |
42
- +-----------------------------------------------------------------+
12
+ HTTP Request GET /products?filter={"where":{"status":"active"},"limit":10}
13
+ |
14
+ v
15
+ Controller Layer Validates via Zod (FilterSchema), parses JSON string -> Filter object
16
+ |
17
+ v
18
+ Service Layer Optional - business logic, authorization, may edit the filter
19
+ |
20
+ v
21
+ Repository Layer applyDefaultFilter() merges the @model default filter,
22
+ FilterBuilder converts Filter -> Drizzle query options, executes
43
23
  ```
44
24
 
25
+ ## Controller layer
45
26
 
46
- ## Controller Layer
27
+ ### `ControllerFactory` (recommended)
47
28
 
48
- ### Using ControllerFactory (Recommended)
49
-
50
- The `ControllerFactory` automatically handles filter parsing and validation:
29
+ `ControllerFactory.defineCrudController` generates a full CRUD controller, including filter parsing and validation, from an entity and a repository binding:
51
30
 
52
31
  ```typescript
53
32
  // src/controllers/product.controller.ts
@@ -77,10 +56,7 @@ const _Controller = ControllerFactory.defineCrudController({
77
56
  export class ProductController extends _Controller {
78
57
  constructor(
79
58
  @inject({
80
- key: BindingKeys.build({
81
- namespace: BindingNamespaces.REPOSITORY,
82
- key: ProductRepository.name,
83
- }),
59
+ key: BindingKeys.build({ namespace: BindingNamespaces.REPOSITORY, key: ProductRepository.name }),
84
60
  })
85
61
  repository: ProductRepository,
86
62
  ) {
@@ -89,16 +65,22 @@ export class ProductController extends _Controller {
89
65
  }
90
66
  ```
91
67
 
92
- **Generated Endpoints:**
68
+ **Filter-bearing endpoints generated:**
69
+
70
+ | Method | Endpoint | Query param |
71
+ |---|---|---|
72
+ | GET | `/products` | `filter` |
73
+ | GET | `/products/{id}` | `filter` (`where` is ignored - the id is the condition) |
74
+ | GET | `/products/find-one` | `filter` |
75
+ | GET | `/products/count` | `where` |
93
76
 
94
- | Method | Endpoint | Filter Location |
95
- |--------|----------|-----------------|
96
- | GET | `/products` | Query param: `?filter={...}` |
97
- | GET | `/products/{id}` | Query param: `?filter={...}` (for includes) |
98
- | GET | `/products/find-one` | Query param: `?filter={...}` |
99
- | GET | `/products/count` | Query param: `?where={...}` |
77
+ - **`isStrict.requestSchema`** controls whether these query params are Zod-required or optional.
78
+ - **`isStrict.path`** controls trailing-slash strictness.
79
+ - **Write endpoints** (`POST /`, `PATCH /{id}`, `DELETE /{id}`, ...) are also generated by the factory, and take no filter.
100
80
 
101
- ### Custom Controller with Manual Filter Handling
81
+ ### Custom controller with manual filter handling
82
+
83
+ For a route outside the generated CRUD set, accept `FilterSchema` directly:
102
84
 
103
85
  ```typescript
104
86
  import { z } from '@hono/zod-openapi';
@@ -118,12 +100,10 @@ export class ProductController extends BaseRestController {
118
100
  configs: {
119
101
  path: '/search',
120
102
  method: 'get',
121
- request: {
122
- query: z.object({ filter: FilterSchema }),
123
- },
103
+ request: { query: z.object({ filter: FilterSchema }) },
124
104
  responses: jsonResponse({ schema: z.array(z.object({ id: z.string() })) }),
125
105
  },
126
- handler: async (context) => {
106
+ handler: async context => {
127
107
  const { filter = {} } = context.req.valid('query');
128
108
  const results = await this._productRepository.find({ filter });
129
109
  return context.json(results);
@@ -133,25 +113,23 @@ export class ProductController extends BaseRestController {
133
113
  }
134
114
  ```
135
115
 
116
+ ## Filter schema validation
136
117
 
137
- ## Filter Schema Validation
138
-
139
- The `FilterSchema` (Zod) accepts both object and JSON string formats:
118
+ `FilterSchema` and `WhereSchema` are both `z.union([<object shape>, <JSON string>.transform(JSON.parse)])` - they accept either format, so a Hono query param (always a string) and a filter built in code both validate:
140
119
 
141
120
  ```typescript
142
- // Object format (from parsed query params)
121
+ // Object format (built in code)
143
122
  { where: { status: 'active' }, limit: 10 }
144
123
 
145
- // JSON string format (from URL query string)
124
+ // JSON string format (from a URL query string)
146
125
  '{"where":{"status":"active"},"limit":10}'
147
126
  ```
148
127
 
149
- The `WhereSchema` also accepts both formats independently, useful for the `count` endpoint which takes `where` directly.
128
+ `WhereSchema` is independent of `FilterSchema` - it backs the `count` endpoint, which takes `where` directly rather than a full filter.
150
129
 
130
+ ## Service layer
151
131
 
152
- ## Service Layer
153
-
154
- Services can modify filters before passing to repositories:
132
+ A service can rewrite the filter before it reaches the repository - useful when a constraint is a caller/session concern rather than a per-model constant (see [Default Filter](./default-filter) for the model-level alternative):
155
133
 
156
134
  ```typescript
157
135
  @service()
@@ -161,83 +139,53 @@ export class ProductService {
161
139
  private _productRepository: ProductRepository,
162
140
  ) {}
163
141
 
164
- async findProducts(filter: TFilter<TProductSchema> = {}) {
165
- // Merge user filter with soft-delete condition
166
- const enhancedFilter: TFilter<TProductSchema> = {
167
- ...filter,
168
- where: {
169
- ...filter.where,
170
- deletedAt: { is: null },
171
- },
172
- };
173
-
174
- return this._productRepository.find({ filter: enhancedFilter });
175
- }
176
-
177
- async findProductsForTenant(
178
- tenantId: string,
179
- filter: TFilter<TProductSchema> = {},
180
- ) {
181
- const isolatedFilter: TFilter<TProductSchema> = {
182
- ...filter,
183
- where: {
184
- ...filter.where,
185
- tenantId,
186
- },
187
- };
188
-
189
- return this._productRepository.find({ filter: isolatedFilter });
142
+ async findProductsForTenant(tenantId: string, filter: TFilter<TProductSchema> = {}) {
143
+ return this._productRepository.find({
144
+ filter: { ...filter, where: { ...filter.where, tenantId } },
145
+ });
190
146
  }
191
147
  }
192
148
  ```
193
149
 
194
-
195
- ## HTTP Request Examples
150
+ ## HTTP request examples
196
151
 
197
152
  **cURL:**
198
153
  ```bash
199
- # Simple filter
200
- curl "http://localhost:3000/products?filter=%7B%22where%22%3A%7B%22status%22%3A%22active%22%7D%2C%22limit%22%3A10%7D"
201
-
202
- # Decoded filter: {"where":{"status":"active"},"limit":10}
203
-
204
- # Complex filter with URL encoding
205
154
  curl -G "http://localhost:3000/products" \
206
155
  --data-urlencode 'filter={"where":{"price":{"gte":100,"lte":500},"tags":{"contains":["featured"]}},"order":["price ASC"],"limit":20}'
207
156
  ```
208
157
 
209
- **JavaScript/TypeScript:**
158
+ **Fetch/Axios:**
210
159
  ```typescript
211
- // Using fetch
212
- const filter = {
213
- where: { status: 'active', price: { lte: 100 } },
214
- order: ['createdAt DESC'],
215
- limit: 10,
216
- };
217
-
218
- const response = await fetch(
219
- `/api/products?filter=${encodeURIComponent(JSON.stringify(filter))}`
220
- );
221
-
222
- // Using axios
223
- const response = await axios.get('/api/products', {
224
- params: { filter: JSON.stringify(filter) },
225
- });
160
+ const filter = { where: { status: 'active', price: { lte: 100 } }, order: ['createdAt DESC'], limit: 10 };
161
+
162
+ // fetch
163
+ const response = await fetch(`/api/products?filter=${encodeURIComponent(JSON.stringify(filter))}`);
164
+
165
+ // axios
166
+ const response = await axios.get('/api/products', { params: { filter: JSON.stringify(filter) } });
226
167
  ```
227
168
 
169
+ ## Debugging a filter
228
170
 
229
- ## Debugging Filters
171
+ `buildQuery` compiles a filter into Drizzle query options without executing it - the fastest way to check what a filter actually resolves to:
230
172
 
231
173
  ```typescript
232
- // Enable logging to see generated SQL
233
- const result = await repository.find({
234
- filter: complexFilter,
235
- options: {
236
- log: { use: true, level: 'debug' },
237
- },
238
- });
239
-
240
- // Or use buildQuery to inspect without executing
241
174
  const queryOptions = repository.buildQuery({ filter: complexFilter });
242
175
  console.log('Generated query options:', queryOptions);
243
176
  ```
177
+
178
+ `options.log` also traces repository calls, but only on the write path (`create`/`updateById`/`updateAll`/`deleteById`/`deleteAll`) - `find`/`findOne`/`findById`/`count` never read it. See [Advanced Repository Features -> Log option](../repositories/advanced.md#log-option) for the write-side form.
179
+
180
+ ## See also
181
+
182
+ - [Filter System Overview](./) - the `filter` shape and every `where` operator family
183
+ - [Default Filter](./default-filter) - `shouldSkipDefaultFilter`, and the model-level alternative to the service-layer pattern above
184
+ - [Use Case Gallery](./use-cases) - more filter shapes with their generated SQL
185
+ - [Advanced Repository Features](../repositories/advanced.md) - transactions, locking, and the full `log`/`lock` options
186
+
187
+ **Files:**
188
+
189
+ - [`packages/core/src/base/controllers/factory/controller.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/controllers/factory/controller.ts) - `ControllerFactory.defineCrudController`
190
+ - [`packages/core/src/base/repositories/query-schemas/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/query-schemas/filter.ts) - `FilterSchema`, `TFilter`, `TInclusion`
191
+ - [`packages/core/src/connectors/postgres/repositories/core/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/core/base.ts) - `RelationalBaseRepository.buildQuery`
@@ -143,3 +143,15 @@ export const productTable = pgTable('Product', {
143
143
  scores: integer('scores').array(), // integer[]
144
144
  });
145
145
  ```
146
+
147
+ ## See also
148
+
149
+ - [Filter System Overview](./) - the `filter` shape and the full `where` operator table
150
+ - [List Operators](./list-operators) - `in`/`nin` match scalar values against an array, the operators these are not to be confused with
151
+ - [Quick Reference](./quick-reference) - every operator, one line each
152
+
153
+ **Files:**
154
+
155
+ - [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, `buildPgArrayComparison`
156
+ - [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
157
+ - [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators` constants
@@ -116,3 +116,15 @@ Matches records where field does NOT equal the value. Both `ne` and `neq` are al
116
116
  | `gte` | `>=` | Greater than or equal |
117
117
  | `lt` | `<` | Less than |
118
118
  | `lte` | `<=` | Less than or equal |
119
+
120
+ ## See also
121
+
122
+ - [Filter System Overview](./) - the `filter` shape and the full `where` operator table
123
+ - [Range Operators](./range-operators) - `between`/`notBetween`, and the `gte`/`lte` equivalent shown above
124
+ - [Quick Reference](./quick-reference) - every operator, one line each
125
+
126
+ **Files:**
127
+
128
+ - [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
129
+ - [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
130
+ - [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators` constants