@venizia/ignis-docs 0.2.0 → 0.2.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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +8 -4
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +247 -153
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +58 -218
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -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`
|
|
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
|
-
- `packages/core/src/helpers/inversion/container.ts` - Extended `Container` with `ApplicationLogger`
|
|
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`
|
|
16
|
+
- `packages/core-server/src/helpers/inversion/container.ts` - Extended `Container` with `ApplicationLogger`
|
|
17
|
+
- `packages/core-server/src/helpers/inversion/registry.ts` - Extended `MetadataRegistry` (singleton, with model/repository/datasource mixins)
|
|
18
|
+
- `packages/core-server/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-server/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
|
|
|
@@ -74,7 +73,8 @@ const container = new Container({ scope: 'MyApp' }); // scope is optional, defau
|
|
|
74
73
|
|
|
75
74
|
When `container.instantiate(MyClass)` is called:
|
|
76
75
|
|
|
77
|
-
1. **Constructor injection** - Reads `@inject` metadata from the class by parameter index
|
|
76
|
+
1. **Constructor injection** - Reads `@inject` metadata from the class by parameter index. The `Reflect`-stored array is already index-keyed, so there is no sort step; the container resolves each dependency and passes them as constructor arguments.
|
|
77
|
+
- If any index in range has no `@inject` metadata, `instantiate()` throws immediately rather than passing `undefined`.
|
|
78
78
|
2. **Property injection** - After the instance is created, reads property metadata, resolves each dependency, and assigns them directly to the instance properties.
|
|
79
79
|
|
|
80
80
|
```typescript
|
|
@@ -91,22 +91,22 @@ class UserController {
|
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
> [!IMPORTANT]
|
|
94
|
-
> **Every constructor parameter of a container-instantiated class must carry `@inject`.** Mixing decorated and undecorated parameters is forbidden
|
|
94
|
+
> **Every constructor parameter of a container-instantiated class must carry `@inject`.** Mixing decorated and undecorated parameters is forbidden. `@inject` stores its metadata at the parameter's index, so an undecorated parameter leaves a hole in that array. There is no channel through which the container could supply it anyway - it would resolve to `undefined`. `instantiate()` refuses the shape by class name and parameter index instead of silently dereferencing the hole:
|
|
95
95
|
>
|
|
96
96
|
> ```
|
|
97
97
|
> [NoteController] Constructor parameter 0 has no @inject | Every parameter of a container-instantiated
|
|
98
98
|
> class must be decorated - the container cannot supply an undecorated one
|
|
99
99
|
> ```
|
|
100
100
|
>
|
|
101
|
-
> The check lives in `instantiate()`, not in the `@inject` decorator itself
|
|
101
|
+
> The check lives in `instantiate()`, not in the `@inject` decorator itself. Parameter decorators run right-to-left, so when `@inject` on parameter 1 runs, parameter 0 has not been visited yet - nothing at that point can know whether it will end up decorated.
|
|
102
102
|
>
|
|
103
|
-
> This does not apply to `@repository`-decorated classes whose constructor appears undecorated at index 0
|
|
103
|
+
> This does not apply to `@repository`-decorated classes whose constructor appears undecorated at index 0. The `@repository` decorator programmatically writes that inject metadata (`registry.setInjectMetadata({ target, index: 0, ... })`), even though no literal `@inject` appears in source. See [Repositories](./repositories/).
|
|
104
104
|
|
|
105
105
|
## `Binding` Class
|
|
106
106
|
|
|
107
107
|
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
108
|
|
|
109
|
-
**File:** `packages/inversion/src/container.ts`
|
|
109
|
+
**File:** `packages/inversion/src/modules/container/index.ts`
|
|
110
110
|
|
|
111
111
|
The `Binding` class extends `BaseHelper`.
|
|
112
112
|
|
|
@@ -206,7 +206,7 @@ This is also used internally by `container.get()` and `container.getBinding()` w
|
|
|
206
206
|
|
|
207
207
|
The `@inject` decorator marks where dependencies should be injected - either on constructor parameters or class properties.
|
|
208
208
|
|
|
209
|
-
**File:** `packages/inversion/src/metadata/injectors.ts` (base) & `packages/core/src/base/metadata/injectors.ts` (core wrapper)
|
|
209
|
+
**File:** `packages/inversion/src/modules/metadata/injectors.ts` (base) & `packages/core-server/src/base/metadata/injectors.ts` (core wrapper)
|
|
210
210
|
|
|
211
211
|
### Signature
|
|
212
212
|
|
|
@@ -257,30 +257,14 @@ The `@venizia/ignis-inversion` package exports base decorators that use the modu
|
|
|
257
257
|
|
|
258
258
|
**Always import from `@venizia/ignis` in application code:**
|
|
259
259
|
```typescript
|
|
260
|
-
import { inject
|
|
260
|
+
import { inject } from '@venizia/ignis';
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
##
|
|
263
|
+
## Registering a Class
|
|
264
264
|
|
|
265
|
-
|
|
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
|
|
265
|
+
No class decorator is needed to make a class injectable. A class becomes resolvable once a binding exists for it. That binding can come from boot auto-discovery, from a framework helper (`app.controller()`, `app.service()`, `@repository`), or explicitly from `container.bind()`. Scope is configured on the binding, never on the class:
|
|
281
266
|
|
|
282
267
|
```typescript
|
|
283
|
-
@injectable({ scope: BindingScopes.SINGLETON })
|
|
284
268
|
class UserService extends BaseService {
|
|
285
269
|
constructor(
|
|
286
270
|
@inject({ key: 'repositories.UserRepository' })
|
|
@@ -289,15 +273,19 @@ class UserService extends BaseService {
|
|
|
289
273
|
super({ scope: UserService.name });
|
|
290
274
|
}
|
|
291
275
|
}
|
|
276
|
+
|
|
277
|
+
app.bind({ key: 'services.UserService' })
|
|
278
|
+
.toClass(UserService)
|
|
279
|
+
.setScope(BindingScopes.SINGLETON); // default is TRANSIENT
|
|
292
280
|
```
|
|
293
281
|
|
|
294
282
|
## `MetadataRegistry`
|
|
295
283
|
|
|
296
|
-
The `MetadataRegistry` stores and retrieves all metadata attached by decorators (`@inject`, `@
|
|
284
|
+
The `MetadataRegistry` stores and retrieves all metadata attached by decorators (`@inject`, `@controller`, `@model`, etc.).
|
|
297
285
|
|
|
298
286
|
### Base MetadataRegistry
|
|
299
287
|
|
|
300
|
-
**File:** `packages/inversion/src/registry.ts`
|
|
288
|
+
**File:** `packages/inversion/src/modules/registry/index.ts`
|
|
301
289
|
|
|
302
290
|
A singleton exported as `metadataRegistry`. Extends `BaseHelper`.
|
|
303
291
|
|
|
@@ -315,12 +303,10 @@ A singleton exported as `metadataRegistry`. Extends `BaseHelper`.
|
|
|
315
303
|
| `setPropertyMetadata({ target, propertyName, metadata })` | Stores property injection metadata (`IPropertyMetadata`). |
|
|
316
304
|
| `getPropertiesMetadata({ target })` | Returns a `Map<string \| symbol, IPropertyMetadata>` for all injected properties. |
|
|
317
305
|
| `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
306
|
|
|
321
307
|
### Core MetadataRegistry
|
|
322
308
|
|
|
323
|
-
**File:** `packages/core/src/helpers/inversion/registry.ts`
|
|
309
|
+
**File:** `packages/core-server/src/helpers/inversion/registry.ts`
|
|
324
310
|
|
|
325
311
|
Extends the base with controller, repository, model, and datasource metadata support via mixins. Accessed as a singleton via `MetadataRegistry.getInstance()`.
|
|
326
312
|
|
|
@@ -333,12 +319,11 @@ Additional capabilities include:
|
|
|
333
319
|
|
|
334
320
|
### Metadata Keys
|
|
335
321
|
|
|
336
|
-
Defined in `packages/inversion/src/common/
|
|
322
|
+
Defined in `packages/inversion/src/modules/metadata/common/constants.ts`:
|
|
337
323
|
|
|
338
324
|
```typescript
|
|
339
325
|
MetadataKeys.PROPERTIES = Symbol.for('ignis:properties')
|
|
340
326
|
MetadataKeys.INJECT = Symbol.for('ignis:inject')
|
|
341
|
-
MetadataKeys.INJECTABLE = Symbol.for('ignis:injectable')
|
|
342
327
|
```
|
|
343
328
|
|
|
344
329
|
### Key Types
|
|
@@ -355,11 +340,6 @@ interface IPropertyMetadata {
|
|
|
355
340
|
isOptional?: boolean;
|
|
356
341
|
[key: string]: any;
|
|
357
342
|
}
|
|
358
|
-
|
|
359
|
-
interface IInjectableMetadata {
|
|
360
|
-
scope?: TBindingScope;
|
|
361
|
-
tags?: Record<string, any>;
|
|
362
|
-
}
|
|
363
343
|
```
|
|
364
344
|
|
|
365
345
|
## Boot System Integration
|
|
@@ -436,7 +416,6 @@ The boot system integrates into the application lifecycle:
|
|
|
436
416
|
app.bind({ key: 'controllers.UserController' }).toClass(UserController);
|
|
437
417
|
|
|
438
418
|
// 4. Later, when UserController is instantiated:
|
|
439
|
-
@injectable()
|
|
440
419
|
class UserController {
|
|
441
420
|
constructor(
|
|
442
421
|
@inject({ key: 'services.UserService' })
|
|
@@ -6,48 +6,25 @@ difficulty: intermediate
|
|
|
6
6
|
|
|
7
7
|
# Using Filters in Your Application
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## Architecture Overview
|
|
9
|
+
A `filter` starts as a JSON-encoded query string on an HTTP request and ends as a Drizzle query. Here is where to hook into each layer in between.
|
|
13
10
|
|
|
14
11
|
```
|
|
15
|
-
|
|
16
|
-
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
+
## Generate a CRUD controller from an entity
|
|
45
26
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### Using ControllerFactory (Recommended)
|
|
49
|
-
|
|
50
|
-
The `ControllerFactory` automatically handles filter parsing and validation:
|
|
27
|
+
For a standard `GET`/`POST`/`PATCH`/`DELETE` resource, generate the controller instead of writing filter parsing by hand. `ControllerFactory.defineCrudController` builds it from an entity and a repository binding:
|
|
51
28
|
|
|
52
29
|
```typescript
|
|
53
30
|
// src/controllers/product.controller.ts
|
|
@@ -77,10 +54,7 @@ const _Controller = ControllerFactory.defineCrudController({
|
|
|
77
54
|
export class ProductController extends _Controller {
|
|
78
55
|
constructor(
|
|
79
56
|
@inject({
|
|
80
|
-
key: BindingKeys.build({
|
|
81
|
-
namespace: BindingNamespaces.REPOSITORY,
|
|
82
|
-
key: ProductRepository.name,
|
|
83
|
-
}),
|
|
57
|
+
key: BindingKeys.build({ namespace: BindingNamespaces.REPOSITORY, key: ProductRepository.name }),
|
|
84
58
|
})
|
|
85
59
|
repository: ProductRepository,
|
|
86
60
|
) {
|
|
@@ -89,20 +63,43 @@ export class ProductController extends _Controller {
|
|
|
89
63
|
}
|
|
90
64
|
```
|
|
91
65
|
|
|
92
|
-
|
|
66
|
+
This generates every filter-bearing endpoint the resource needs:
|
|
67
|
+
|
|
68
|
+
| Method | Endpoint | Query param |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| GET | `/products` | `filter` |
|
|
71
|
+
| GET | `/products/{id}` | `filter` (`where` is ignored - the id is the condition) |
|
|
72
|
+
| GET | `/products/find-one` | `filter` |
|
|
73
|
+
| GET | `/products/count` | `where` |
|
|
74
|
+
|
|
75
|
+
Set `isStrict.requestSchema: true` to make these query params Zod-required; set `isStrict.path: true` to reject trailing-slash variants of the route. Write endpoints (`POST /`, `PATCH /{id}`, `DELETE /{id}`, ...) come from the same factory call and take no filter.
|
|
76
|
+
|
|
77
|
+
## Map a request query string to a parsed filter
|
|
78
|
+
|
|
79
|
+
`FilterSchema` and `WhereSchema` both accept a JSON string or a plain object, so the same schema validates a Hono query param (always a string) and a filter built in code:
|
|
80
|
+
|
|
81
|
+
| Request | Parsed filter |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `GET /products?filter={"where":{"status":"active"}}` | `{ where: { status: 'active' } }` |
|
|
84
|
+
| `GET /products?filter={"limit":10,"skip":20}` | `{ limit: 10, skip: 20 }` |
|
|
85
|
+
| `GET /products?filter={"where":{"price":{"gte":100,"lte":500}},"order":["price ASC"]}` | `{ where: { price: { gte: 100, lte: 500 } }, order: ['price ASC'] }` |
|
|
86
|
+
| `GET /products/count?where={"role":"admin"}` | `{ role: 'admin' }` |
|
|
87
|
+
|
|
88
|
+
`WhereSchema` is independent of `FilterSchema` - it backs the `count` endpoint, which takes `where` directly rather than a full filter.
|
|
93
89
|
|
|
94
|
-
|
|
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={...}` |
|
|
90
|
+
## Write a custom route that accepts a filter
|
|
100
91
|
|
|
101
|
-
|
|
92
|
+
For a route outside the generated CRUD set, use `FilterQuerySchema` - the same query shape the generated `find` route takes:
|
|
102
93
|
|
|
103
94
|
```typescript
|
|
104
95
|
import { z } from '@hono/zod-openapi';
|
|
105
|
-
import {
|
|
96
|
+
import {
|
|
97
|
+
BaseRestController,
|
|
98
|
+
controller,
|
|
99
|
+
FilterQuerySchema,
|
|
100
|
+
inject,
|
|
101
|
+
jsonResponse,
|
|
102
|
+
} from '@venizia/ignis';
|
|
106
103
|
|
|
107
104
|
@controller({ path: '/products' })
|
|
108
105
|
export class ProductController extends BaseRestController {
|
|
@@ -118,12 +115,10 @@ export class ProductController extends BaseRestController {
|
|
|
118
115
|
configs: {
|
|
119
116
|
path: '/search',
|
|
120
117
|
method: 'get',
|
|
121
|
-
request: {
|
|
122
|
-
query: z.object({ filter: FilterSchema }),
|
|
123
|
-
},
|
|
118
|
+
request: { query: FilterQuerySchema },
|
|
124
119
|
responses: jsonResponse({ schema: z.array(z.object({ id: z.string() })) }),
|
|
125
120
|
},
|
|
126
|
-
handler: async
|
|
121
|
+
handler: async context => {
|
|
127
122
|
const { filter = {} } = context.req.valid('query');
|
|
128
123
|
const results = await this._productRepository.find({ filter });
|
|
129
124
|
return context.json(results);
|
|
@@ -133,25 +128,29 @@ export class ProductController extends BaseRestController {
|
|
|
133
128
|
}
|
|
134
129
|
```
|
|
135
130
|
|
|
131
|
+
### The two query shapes
|
|
136
132
|
|
|
137
|
-
|
|
133
|
+
Reach for these instead of rebuilding the same object at every route.
|
|
138
134
|
|
|
139
|
-
|
|
135
|
+
| Schema | Query shape | Use it for |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `FilterQuerySchema` | `{ filter?: TFilter }` | Any route that takes a full filter |
|
|
138
|
+
| `WhereQuerySchema` | `{ where?: TWhere }` | Any route that takes conditions and no pagination |
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
// Object format (from parsed query params)
|
|
143
|
-
{ where: { status: 'active' }, limit: 10 }
|
|
140
|
+
Both are plain Zod objects, so a route that takes more than one parameter extends rather than rebuilds:
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
```typescript
|
|
143
|
+
request: { query: WhereQuerySchema.extend({ q: z.string().max(255).optional() }) },
|
|
147
144
|
```
|
|
148
145
|
|
|
149
|
-
|
|
146
|
+
Neither needs an extra `.optional()`. `FilterSchema` already carries one, so `FilterSchema.optional()` is the same schema written longer, and a trailing `.partial()` on a single optional key does nothing.
|
|
150
147
|
|
|
148
|
+
> [!NOTE]
|
|
149
|
+
> The generated `updateBy` and `deleteBy` routes deliberately do not use `WhereQuerySchema`. They require `where`, because a missing one rewrites or deletes every row in the table.
|
|
151
150
|
|
|
152
|
-
##
|
|
151
|
+
## Rewrite a filter before it reaches the repository
|
|
153
152
|
|
|
154
|
-
|
|
153
|
+
Add a service between the controller and the repository when a constraint is a caller or session concern rather than a per-model constant - a tenant ID pulled from the request context, for example. Use [Default Filter](./default-filter) instead when the constraint applies to every caller of the model:
|
|
155
154
|
|
|
156
155
|
```typescript
|
|
157
156
|
@service()
|
|
@@ -161,83 +160,56 @@ export class ProductService {
|
|
|
161
160
|
private _productRepository: ProductRepository,
|
|
162
161
|
) {}
|
|
163
162
|
|
|
164
|
-
async
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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 });
|
|
163
|
+
async findProductsForTenant(tenantId: string, filter: TFilter<TProductSchema> = {}) {
|
|
164
|
+
return this._productRepository.find({
|
|
165
|
+
filter: { ...filter, where: { ...filter.where, tenantId } },
|
|
166
|
+
});
|
|
190
167
|
}
|
|
191
168
|
}
|
|
192
169
|
```
|
|
193
170
|
|
|
171
|
+
## Build a filter query string from a client
|
|
194
172
|
|
|
195
|
-
|
|
173
|
+
Encode the filter as JSON and pass it as the `filter` query param, whichever HTTP client you use:
|
|
196
174
|
|
|
197
175
|
**cURL:**
|
|
198
176
|
```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
177
|
curl -G "http://localhost:3000/products" \
|
|
206
178
|
--data-urlencode 'filter={"where":{"price":{"gte":100,"lte":500},"tags":{"contains":["featured"]}},"order":["price ASC"],"limit":20}'
|
|
207
179
|
```
|
|
208
180
|
|
|
209
|
-
**
|
|
181
|
+
**Fetch/Axios:**
|
|
210
182
|
```typescript
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
});
|
|
183
|
+
const filter = { where: { status: 'active', price: { lte: 100 } }, order: ['createdAt DESC'], limit: 10 };
|
|
184
|
+
|
|
185
|
+
// fetch
|
|
186
|
+
const response = await fetch(`/api/products?filter=${encodeURIComponent(JSON.stringify(filter))}`);
|
|
187
|
+
|
|
188
|
+
// axios
|
|
189
|
+
const response = await axios.get('/api/products', { params: { filter: JSON.stringify(filter) } });
|
|
226
190
|
```
|
|
227
191
|
|
|
192
|
+
## Debug what a filter compiles to
|
|
228
193
|
|
|
229
|
-
|
|
194
|
+
Call `buildQuery` to compile a filter into Drizzle query options without executing it - the fastest way to check what a filter actually resolves to:
|
|
230
195
|
|
|
231
196
|
```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
197
|
const queryOptions = repository.buildQuery({ filter: complexFilter });
|
|
242
198
|
console.log('Generated query options:', queryOptions);
|
|
243
199
|
```
|
|
200
|
+
|
|
201
|
+
`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.
|
|
202
|
+
|
|
203
|
+
## See also
|
|
204
|
+
|
|
205
|
+
- [Filter System Overview](./) - the `filter` shape and every `where` operator family
|
|
206
|
+
- [Default Filter](./default-filter) - `shouldSkipDefaultFilter`, and the model-level alternative to the service-layer rewrite above
|
|
207
|
+
- [Use Case Gallery](./use-cases) - more filter shapes with their generated SQL
|
|
208
|
+
- [Advanced Repository Features](../repositories/advanced.md) - transactions, locking, and the full `log`/`lock` options
|
|
209
|
+
|
|
210
|
+
**Files:**
|
|
211
|
+
|
|
212
|
+
- [`packages/kernel/src/base/controllers/factory/controller.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/kernel/src/base/controllers/factory/controller.ts) - `ControllerFactory.defineCrudController`
|
|
213
|
+
- [`packages/kernel/src/base/repositories/query-schemas/index.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/kernel/src/base/repositories/query-schemas/index.ts) - `FilterSchema`, `WhereSchema`, `FilterQuerySchema`, `WhereQuerySchema`
|
|
214
|
+
- [`packages/filter/src/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/types.ts) - `TFilter`, `TInclusion`
|
|
215
|
+
- [`packages/connectors/src/relational/core/repositories/core/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/connectors/src/relational/core/repositories/core/base.ts) - `RelationalBaseRepository.buildQuery`
|
|
@@ -23,44 +23,34 @@ Find rows where the array column contains **all** specified elements.
|
|
|
23
23
|
// Schema: tags varchar(100)[]
|
|
24
24
|
// Data: Product A has ['electronics', 'featured', 'sale']
|
|
25
25
|
|
|
26
|
-
// Find products with BOTH 'electronics' AND 'featured'
|
|
27
26
|
{ where: { tags: { contains: ['electronics', 'featured'] } } }
|
|
28
27
|
// SQL: "tags"::text[] @> ARRAY['electronics', 'featured']::text[]
|
|
29
|
-
|
|
30
|
-
// Single element (can pass single value or array)
|
|
31
|
-
{ where: { tags: { contains: ['featured'] } } }
|
|
32
|
-
{ where: { tags: { contains: 'featured' } } } // Also works
|
|
33
|
-
// Matches: ['featured'], ['featured', 'sale'], ['a', 'featured', 'b']
|
|
34
28
|
```
|
|
35
29
|
|
|
30
|
+
> [!NOTE]
|
|
31
|
+
> A single value is wrapped in an array automatically: `{ contains: 'featured' }` is treated as `{ contains: ['featured'] }`.
|
|
32
|
+
|
|
36
33
|
|
|
37
34
|
## containedBy (<@)
|
|
38
35
|
|
|
39
36
|
Find rows where **all** array elements are within the specified set.
|
|
40
37
|
|
|
41
38
|
```typescript
|
|
42
|
-
// Find products where ALL tags are in the allowed list
|
|
43
39
|
{ where: { tags: { containedBy: ['sale', 'featured', 'new', 'popular'] } } }
|
|
44
40
|
// SQL: "tags"::text[] <@ ARRAY['sale', 'featured', 'new', 'popular']::text[]
|
|
45
|
-
|
|
46
|
-
// Product A ['featured', 'sale'] -> matches (all in list)
|
|
47
|
-
// Product B ['featured', 'clearance'] -> no match ('clearance' not in list)
|
|
48
|
-
// Product C [] -> matches (empty is subset of everything)
|
|
49
41
|
```
|
|
50
42
|
|
|
43
|
+
> [!NOTE]
|
|
44
|
+
> An empty array is a subset of every set, so `tags: []` always matches `containedBy`.
|
|
45
|
+
|
|
51
46
|
|
|
52
47
|
## overlaps (&&)
|
|
53
48
|
|
|
54
49
|
Find rows where the arrays share at least one common element.
|
|
55
50
|
|
|
56
51
|
```typescript
|
|
57
|
-
// Find products with 'premium' OR 'sale' tag
|
|
58
52
|
{ where: { tags: { overlaps: ['premium', 'sale'] } } }
|
|
59
53
|
// SQL: "tags"::text[] && ARRAY['premium', 'sale']::text[]
|
|
60
|
-
|
|
61
|
-
// Product A ['featured', 'sale'] -> matches (has 'sale')
|
|
62
|
-
// Product B ['premium', 'luxury'] -> matches (has 'premium')
|
|
63
|
-
// Product C ['new', 'featured'] -> no match (no overlap)
|
|
64
54
|
```
|
|
65
55
|
|
|
66
56
|
|
|
@@ -84,52 +74,41 @@ Find rows where the arrays share at least one common element.
|
|
|
84
74
|
| "Must have AT LEAST ONE of these tags" | `overlaps` |
|
|
85
75
|
|
|
86
76
|
|
|
87
|
-
## Empty Array Behavior
|
|
88
|
-
|
|
89
|
-
| Operator | SQL Generated | Behavior |
|
|
90
|
-
|----------|---------------|----------|
|
|
91
|
-
| `contains: []` | `WHERE true` | Returns **ALL** rows |
|
|
92
|
-
| `containedBy: []` | `WHERE "col" = '{}'` | Returns only rows with **empty arrays** |
|
|
93
|
-
| `overlaps: []` | `WHERE false` | Returns **NO** rows |
|
|
94
|
-
|
|
95
|
-
> [!NOTE]
|
|
96
|
-
> Single values are automatically wrapped in an array: `{ contains: 'value' }` is treated as `{ contains: ['value'] }`.
|
|
97
|
-
|
|
98
|
-
|
|
99
77
|
## Type Handling
|
|
100
78
|
|
|
101
|
-
|
|
79
|
+
The element type of the array column decides the cast in the generated SQL.
|
|
80
|
+
|
|
102
81
|
```typescript
|
|
82
|
+
// String arrays (varchar[], text[], char[]) - both sides cast to text[]
|
|
103
83
|
{ where: { tags: { contains: ['a', 'b'] } } }
|
|
104
84
|
// SQL: "tags"::text[] @> ARRAY['a', 'b']::text[]
|
|
105
|
-
```
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
**Numeric Arrays** (`integer[]`, `numeric[]`):
|
|
110
|
-
```typescript
|
|
86
|
+
// Numeric arrays (integer[], numeric[]) - no cast needed
|
|
111
87
|
{ where: { scores: { contains: [100, 200] } } }
|
|
112
88
|
// SQL: "scores" @> ARRAY[100, 200]
|
|
113
|
-
```
|
|
114
89
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
**Boolean Arrays**:
|
|
118
|
-
```typescript
|
|
90
|
+
// Boolean arrays - no cast needed
|
|
119
91
|
{ where: { flags: { contains: [true, false] } } }
|
|
120
92
|
// SQL: "flags" @> ARRAY[true, false]
|
|
121
93
|
```
|
|
122
94
|
|
|
123
95
|
|
|
96
|
+
## Empty Array Behavior
|
|
97
|
+
|
|
98
|
+
| Operator | SQL generated | Behavior |
|
|
99
|
+
|----------|---------------|----------|
|
|
100
|
+
| `contains: []` | `WHERE true` | Returns **ALL** rows |
|
|
101
|
+
| `containedBy: []` | `WHERE "col" = '{}'` | Returns only rows with **empty arrays** |
|
|
102
|
+
| `overlaps: []` | `WHERE false` | Returns **NO** rows |
|
|
103
|
+
|
|
104
|
+
|
|
124
105
|
## Security: Parameterized Values
|
|
125
106
|
|
|
126
|
-
Every element of `contains`/`containedBy`/`overlaps` is bound as a query parameter
|
|
107
|
+
Every element of `contains`/`containedBy`/`overlaps` is bound as a query parameter - only the operator token (`@>`/`<@`/`&&`) is raw SQL. See [The Hardening Round](../../../changelogs/2026-07-13-hardening-round) for the prior injection this closed.
|
|
127
108
|
|
|
128
109
|
|
|
129
110
|
## Defining Array Columns
|
|
130
111
|
|
|
131
|
-
In your Drizzle schema:
|
|
132
|
-
|
|
133
112
|
```typescript
|
|
134
113
|
import { pgTable, text, varchar, integer } from 'drizzle-orm/pg-core';
|
|
135
114
|
|
|
@@ -137,9 +116,20 @@ export const productTable = pgTable('Product', {
|
|
|
137
116
|
id: text('id').primaryKey(),
|
|
138
117
|
name: text('name').notNull(),
|
|
139
118
|
|
|
140
|
-
// Array columns
|
|
141
119
|
tags: varchar('tags', { length: 100 }).array(), // varchar(100)[]
|
|
142
120
|
categories: text('categories').array(), // text[]
|
|
143
121
|
scores: integer('scores').array(), // integer[]
|
|
144
122
|
});
|
|
145
123
|
```
|
|
124
|
+
|
|
125
|
+
## See also
|
|
126
|
+
|
|
127
|
+
- [Filter System Overview](./) - the `filter` shape and the full `where` operator table
|
|
128
|
+
- [List Operators](./list-operators) - `in`/`nin` match scalar values against an array, the operators these are not to be confused with
|
|
129
|
+
- [Quick Reference](./quick-reference) - every operator, one line each
|
|
130
|
+
|
|
131
|
+
**Files:**
|
|
132
|
+
|
|
133
|
+
- [`packages/core-server/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, `buildPgArrayComparison`
|
|
134
|
+
- [`packages/core-server/src/connectors/relational/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
|
|
135
|
+
- [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `QueryOperators` constants
|