@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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- 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 +26 -2
- 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 +6 -2
- 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 +182 -93
- 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 +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- 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/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- 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 +57 -218
- 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/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`
|
|
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`
|
|
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
|
|
259
|
+
import { inject } from '@venizia/ignis';
|
|
261
260
|
```
|
|
262
261
|
|
|
263
|
-
##
|
|
262
|
+
## Registering a Class
|
|
264
263
|
|
|
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
|
|
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`, `@
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
|
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
|
+
## Controller layer
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
### `ControllerFactory` (recommended)
|
|
47
28
|
|
|
48
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 });
|
|
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
|
-
**
|
|
158
|
+
**Fetch/Axios:**
|
|
210
159
|
```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
|
-
});
|
|
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
|
-
|
|
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
|