@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
|
@@ -11,13 +11,13 @@ Technical reference for gRPC controller classes -- the foundation for building g
|
|
|
11
11
|
IGNIS gRPC controllers follow the same patterns as REST controllers (decorator-based routing, `binding()` method, DI integration) while bridging to ConnectRPC's universal handler system. REST and gRPC controllers coexist in the same application, sharing the same DI container, middleware pipeline, and lifecycle.
|
|
12
12
|
|
|
13
13
|
**Files:**
|
|
14
|
-
- `packages/core/src/base/controllers/grpc/abstract.ts`
|
|
15
|
-
- `packages/core/src/base/controllers/grpc/base.ts`
|
|
16
|
-
- `packages/core/src/base/controllers/grpc/adapter.ts`
|
|
17
|
-
- `packages/core/src/base/controllers/grpc/common/types.ts`
|
|
18
|
-
- `packages/core/src/base/metadata/routes/rpc.ts`
|
|
19
|
-
- `packages/core/src/components/controller/grpc/grpc.component.ts`
|
|
20
|
-
- `packages/core/src/components/controller/grpc/common/types.ts`
|
|
14
|
+
- `packages/core-server/src/base/controllers/grpc/abstract.ts`
|
|
15
|
+
- `packages/core-server/src/base/controllers/grpc/base.ts`
|
|
16
|
+
- `packages/core-server/src/base/controllers/grpc/adapter.ts`
|
|
17
|
+
- `packages/core-server/src/base/controllers/grpc/common/types.ts`
|
|
18
|
+
- `packages/core-server/src/base/metadata/routes/rpc.ts`
|
|
19
|
+
- `packages/core-server/src/components/controller/grpc/grpc.component.ts`
|
|
20
|
+
- `packages/core-server/src/components/controller/grpc/common/types.ts`
|
|
21
21
|
|
|
22
22
|
## Quick Reference
|
|
23
23
|
|
|
@@ -35,7 +35,7 @@ IGNIS gRPC controllers follow the same patterns as REST controllers (decorator-b
|
|
|
35
35
|
| **@rpc** | Generic method decorator (requires explicit `method` in configs) |
|
|
36
36
|
|
|
37
37
|
> [!WARNING]
|
|
38
|
-
> **Current version supports unary RPCs only.** The `@serverStream`, `@clientStream`, and `@bidiStream` decorators still exist and set metadata correctly
|
|
38
|
+
> **Current version supports unary RPCs only.** The `@serverStream`, `@clientStream`, and `@bidiStream` decorators still exist and set metadata correctly. But `BaseGrpcController.registerRoute()` throws a clear error at boot time if a non-unary RPC is registered - the Connect protocol over HTTP/1.1 cannot support streaming. The decorators are preserved for forward compatibility.
|
|
39
39
|
|
|
40
40
|
## Prerequisites
|
|
41
41
|
|
|
@@ -57,7 +57,7 @@ bun add @connectrpc/connect-web
|
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
> [!NOTE]
|
|
60
|
-
> `@connectrpc/connect` is an **optional** peer dependency of `@venizia/ignis
|
|
60
|
+
> `@connectrpc/connect` is an **optional** peer dependency of `@venizia/ignis`. It is only loaded at runtime when a gRPC controller is configured, via `createRequire` from the application's `node_modules`. If it is missing, `GrpcRequestAdapter.build()` throws a clear error at startup via `ModuleUtility.assertInstalled()`. A compiled binary has no `node_modules` to resolve against and must pass the peer through `IGrpcComponentConfig.module` - see [Peer Dependency Loading](#peer-dependency-loading). `@bufbuild/protobuf` is required by your generated protobuf code (e.g. `create()`), not by the framework itself.
|
|
61
61
|
|
|
62
62
|
### Protobuf Code Generation
|
|
63
63
|
|
|
@@ -229,7 +229,7 @@ The `configure()` method on `AbstractGrpcController` is idempotent (guarded by `
|
|
|
229
229
|
|
|
230
230
|
## RPC Decorators
|
|
231
231
|
|
|
232
|
-
All RPC decorators live in `packages/core/src/base/metadata/routes/rpc.ts`. They register metadata in the `MetadataRegistry`, which is read during `configure()`.
|
|
232
|
+
All RPC decorators live in `packages/core-server/src/base/metadata/routes/rpc.ts`. They register metadata in the `MetadataRegistry`, which is read during `configure()`.
|
|
233
233
|
|
|
234
234
|
### `@rpc` -- Generic
|
|
235
235
|
|
|
@@ -415,7 +415,7 @@ Internal bridge between IGNIS gRPC controllers and ConnectRPC's universal handle
|
|
|
415
415
|
|
|
416
416
|
### Architecture
|
|
417
417
|
|
|
418
|
-
The adapter solves a key challenge: ConnectRPC handlers have their own `(request, context) => response` signature
|
|
418
|
+
The adapter solves a key challenge: ConnectRPC handlers have their own `(request, context) => response` signature. IGNIS controllers, though, need access to the Hono `Context` for middleware, auth, and request-scoped state. The adapter uses `AsyncLocalStorage` to provide request-scoped context isolation, ensuring concurrent requests never share state.
|
|
419
419
|
|
|
420
420
|
```
|
|
421
421
|
Hono Request
|
|
@@ -429,7 +429,7 @@ Hono Request
|
|
|
429
429
|
|
|
430
430
|
### Static `build()` Method
|
|
431
431
|
|
|
432
|
-
The only public API. Validates peer deps via `
|
|
432
|
+
The only public API. Validates peer deps via `ModuleUtility.assertInstalled()`, creates the adapter, and returns the middleware + registered paths:
|
|
433
433
|
|
|
434
434
|
```typescript
|
|
435
435
|
static async build(opts: {
|
|
@@ -462,12 +462,27 @@ The optional `interceptors` array is passed to ConnectRPC's `createConnectRouter
|
|
|
462
462
|
|
|
463
463
|
### Peer Dependency Loading
|
|
464
464
|
|
|
465
|
-
The adapter
|
|
465
|
+
The adapter needs two entry points:
|
|
466
466
|
|
|
467
467
|
- `@connectrpc/connect` -- for `createConnectRouter`
|
|
468
468
|
- `@connectrpc/connect/protocol` -- for `universalServerRequestFromFetch` and `universalServerResponseToFetch`
|
|
469
469
|
|
|
470
|
-
|
|
470
|
+
By default it loads both at runtime using `createRequire` from the application's `node_modules`. That keeps the specifier invisible to `Bun.build`, so a consumer who never uses gRPC is not forced to install the peer.
|
|
471
|
+
|
|
472
|
+
**A compiled application must pass `module`.** A `bun build --compile` binary ships without `node_modules`, so `createRequire` has nothing to resolve against. Hand the peer over through the component options instead - the static import is what embeds it in the binary:
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
import * as connect from '@connectrpc/connect';
|
|
476
|
+
import * as protocol from '@connectrpc/connect/protocol';
|
|
477
|
+
|
|
478
|
+
this.bind({ key: GrpcBindingKeys.GRPC_COMPONENT_OPTIONS }).toValue({
|
|
479
|
+
module: { connect, protocol },
|
|
480
|
+
});
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
`GrpcComponent` assigns the module to each controller before `configure()`, and the adapter skips both `assertInstalled` and `createRequire` when it is present.
|
|
484
|
+
|
|
485
|
+
`ModuleUtility.register` does not work here. The adapter resolves the specifier itself, so the registry never reaches it.
|
|
471
486
|
|
|
472
487
|
### Error Handling
|
|
473
488
|
|
|
@@ -477,7 +492,7 @@ On handler errors, the adapter returns a JSON response with:
|
|
|
477
492
|
- `grpc-message` header: URL-encoded error message
|
|
478
493
|
- Body: JSON `{ message, code }`
|
|
479
494
|
|
|
480
|
-
The adapter uses a duck-type check on `error.code` to preserve gRPC status codes from ConnectRPC errors without importing `ConnectError` directly
|
|
495
|
+
The adapter uses a duck-type check on `error.code` to preserve gRPC status codes from ConnectRPC errors, without importing `ConnectError` directly. That avoids tight coupling to the peer dependency.
|
|
481
496
|
|
|
482
497
|
## `GrpcComponent`
|
|
483
498
|
|
|
@@ -488,9 +503,30 @@ Auto-discovers and configures gRPC controllers during the application lifecycle.
|
|
|
488
503
|
```typescript
|
|
489
504
|
interface IGrpcComponentConfig {
|
|
490
505
|
interceptors?: unknown[];
|
|
506
|
+
module?: IConnectRpcModule;
|
|
491
507
|
}
|
|
492
508
|
```
|
|
493
509
|
|
|
510
|
+
| Option | Type | Default | Meaning |
|
|
511
|
+
|---|---|---|---|
|
|
512
|
+
| `interceptors` | `unknown[]` | none | ConnectRPC interceptors, passed to `createConnectRouter` for every gRPC controller. An empty list passes nothing |
|
|
513
|
+
| `module` | `IConnectRpcModule` | none | The ConnectRPC peer, as `{ connect, protocol }`. Required for a compiled binary - see [Peer Dependency Loading](#peer-dependency-loading) |
|
|
514
|
+
|
|
515
|
+
Both options are component-wide. `GrpcComponent` assigns them to every gRPC controller it discovers, before calling `configure()`:
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
import type { Interceptor } from '@connectrpc/connect';
|
|
519
|
+
|
|
520
|
+
const logging: Interceptor = next => async request => {
|
|
521
|
+
const response = await next(request);
|
|
522
|
+
return response;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
this.bind({ key: GrpcBindingKeys.GRPC_COMPONENT_OPTIONS }).toValue({
|
|
526
|
+
interceptors: [logging],
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
494
530
|
The component registers a default (empty) config binding under the key `'@app/grpc/options'` (`GrpcBindingKeys.GRPC_COMPONENT_OPTIONS`).
|
|
495
531
|
|
|
496
532
|
### Behavior
|
|
@@ -623,7 +659,7 @@ export class Application extends BaseApplication {
|
|
|
623
659
|
```
|
|
624
660
|
|
|
625
661
|
> [!WARNING]
|
|
626
|
-
> If `transports` does not include `ControllerTransports.GRPC`, gRPC controllers are still registered in the DI container
|
|
662
|
+
> If `transports` does not include `ControllerTransports.GRPC`, gRPC controllers are still registered in the DI container. But the `GrpcComponent` is never mounted -- their `configure()` is never called and no routes are served.
|
|
627
663
|
|
|
628
664
|
### Dual Transport
|
|
629
665
|
|
|
@@ -639,7 +675,7 @@ preConfigure() {
|
|
|
639
675
|
}
|
|
640
676
|
```
|
|
641
677
|
|
|
642
|
-
REST controllers are handled by the `RestComponent
|
|
678
|
+
REST controllers are handled by the `RestComponent`, active when `transports` includes `ControllerTransports.REST` (the default). gRPC controllers are handled by the `GrpcComponent`, active when that transport is enabled. They share the same DI container and lifecycle.
|
|
643
679
|
|
|
644
680
|
## Complete Example
|
|
645
681
|
|
|
@@ -9,7 +9,7 @@ Core classes that power every IGNIS application - from the Application entry poi
|
|
|
9
9
|
|
|
10
10
|
| Class | Purpose | Extends |
|
|
11
11
|
|-------|---------|---------|
|
|
12
|
-
| `BaseApplication` | Application entry point, DI container | `AbstractApplication` |
|
|
12
|
+
| `BaseApplication` | Application entry point, DI container | `ServerApplication` -> `RestApplication` -> `AbstractApplication` |
|
|
13
13
|
| `BaseRestController` | REST/HTTP route handlers | `AbstractRestController` |
|
|
14
14
|
| `BaseGrpcController` | gRPC route handlers (ConnectRPC) | `AbstractGrpcController` |
|
|
15
15
|
| `BaseService` | Business logic layer | - |
|
|
@@ -79,8 +79,10 @@ Core classes that power every IGNIS application - from the Application entry poi
|
|
|
79
79
|
## Class Hierarchy
|
|
80
80
|
|
|
81
81
|
```
|
|
82
|
-
AbstractApplication
|
|
83
|
-
└──
|
|
82
|
+
AbstractApplication (browser-pure, @venizia/ignis-kernel)
|
|
83
|
+
└── RestApplication (browser-pure, owns the router)
|
|
84
|
+
└── ServerApplication (opens the socket)
|
|
85
|
+
└── BaseApplication ──────► Your Application
|
|
84
86
|
|
|
85
87
|
AbstractRepository (engine-neutral, src/base)
|
|
86
88
|
├── PostgresBaseRepository (connectors/postgres)
|
|
@@ -10,10 +10,10 @@ lastUpdated: 2026-06-14
|
|
|
10
10
|
IGNIS provides built-in middleware functions and a provider-based middleware class for handling common HTTP concerns: error handling, request logging, 404 responses, and favicon serving. These are registered automatically by `BaseApplication` during startup - you do not import or wire them manually.
|
|
11
11
|
|
|
12
12
|
**Files:**
|
|
13
|
-
- `packages/core/src/base/middlewares/app-error/app-error.middleware.ts`
|
|
14
|
-
- `packages/core/src/base/middlewares/not-found/not-found.middleware.ts`
|
|
15
|
-
- `packages/core/src/base/middlewares/request-spy/request-spy.middleware.ts`
|
|
16
|
-
- `packages/core/src/base/middlewares/emoji-favicon/emoji-favicon.middleware.ts`
|
|
13
|
+
- `packages/core-server/src/base/middlewares/app-error/app-error.middleware.ts`
|
|
14
|
+
- `packages/core-server/src/base/middlewares/not-found/not-found.middleware.ts`
|
|
15
|
+
- `packages/core-server/src/base/middlewares/request-spy/request-spy.middleware.ts`
|
|
16
|
+
- `packages/core-server/src/base/middlewares/emoji-favicon/emoji-favicon.middleware.ts`
|
|
17
17
|
|
|
18
18
|
## Prerequisites
|
|
19
19
|
|
|
@@ -26,7 +26,7 @@ Before reading this document, you should understand:
|
|
|
26
26
|
|
|
27
27
|
| Middleware | Type | Purpose |
|
|
28
28
|
|-----------|------|---------|
|
|
29
|
-
| `
|
|
29
|
+
| `AppErrorMiddleware` | `IProvider<ErrorHandler>` | Global error handler (Zod, DB constraints, generic) |
|
|
30
30
|
| `notFoundHandler` | `NotFoundHandler` | JSON 404 response for unknown routes |
|
|
31
31
|
| `RequestSpyMiddleware` | `IProvider<MiddlewareHandler>` | Request/response logging with timing |
|
|
32
32
|
| `emojiFavicon` | `MiddlewareHandler` | Serves an emoji as SVG favicon |
|
|
@@ -40,7 +40,7 @@ protected async registerDefaultMiddlewares() {
|
|
|
40
40
|
const server = this.getServer();
|
|
41
41
|
|
|
42
42
|
// 1. Global error handler
|
|
43
|
-
server.onError(
|
|
43
|
+
server.onError(new AppErrorMiddleware({ logger, rootKey }).value());
|
|
44
44
|
|
|
45
45
|
// 2. Async context storage (if enabled)
|
|
46
46
|
if (this.configs.asyncContext?.enable) {
|
|
@@ -60,24 +60,24 @@ protected async registerDefaultMiddlewares() {
|
|
|
60
60
|
|
|
61
61
|
After `registerDefaultMiddlewares()`, the application calls user-defined `staticConfigure()`, `preConfigure()`, and so on. The user's `setupMiddlewares()` hook runs after `initialize()` but before the server starts.
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## AppErrorMiddleware
|
|
64
64
|
|
|
65
|
-
Global error handler registered via `server.onError()`. Handles ZodError validation errors, PostgreSQL constraint violations, and generic errors.
|
|
65
|
+
Global error handler registered via `server.onError()`. Handles ZodError validation errors, PostgreSQL constraint violations, and generic errors. Like `RequestSpyMiddleware`, it is an `IProvider` - build it, then call `value()` for the handler.
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
Registered automatically by `BaseApplication`.
|
|
68
68
|
|
|
69
69
|
### Signature
|
|
70
70
|
|
|
71
71
|
```typescript
|
|
72
|
-
|
|
73
|
-
logger
|
|
74
|
-
|
|
75
|
-
}
|
|
72
|
+
class AppErrorMiddleware extends BaseHelper implements IProvider<ErrorHandler> {
|
|
73
|
+
constructor(opts?: { logger?: ILogger; rootKey?: string });
|
|
74
|
+
value(): ErrorHandler;
|
|
75
|
+
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
| Parameter | Type | Description |
|
|
79
79
|
|-----------|------|-------------|
|
|
80
|
-
| `logger` | `
|
|
80
|
+
| `logger` | `ILogger \| undefined` | Overrides the middleware's own scoped logger - `BaseApplication` passes its own so error lines stay in its scope |
|
|
81
81
|
| `rootKey` | `string \| undefined` | Optional root key to wrap the error response object |
|
|
82
82
|
|
|
83
83
|
### Error Handling Logic
|
|
@@ -86,13 +86,17 @@ function appErrorHandler(opts: {
|
|
|
86
86
|
|
|
87
87
|
When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`.
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
`message` and `normalized.code` come from the first failing issue. `message` is that issue's message; `normalized.code` is its `params.code` if the schema set one, otherwise its raw Zod code. The full per-field list stays under `details.cause`. `normalized.args` is always `{}` - a Zod issue carries no interpolation values.
|
|
90
90
|
|
|
91
91
|
```json
|
|
92
92
|
{
|
|
93
93
|
"message": "Invalid email address",
|
|
94
|
-
"messageCode": "user.email.invalid",
|
|
95
94
|
"statusCode": 422,
|
|
95
|
+
"normalized": {
|
|
96
|
+
"text": "Invalid email address",
|
|
97
|
+
"code": "user.email.invalid",
|
|
98
|
+
"args": {}
|
|
99
|
+
},
|
|
96
100
|
"requestId": "abc-123",
|
|
97
101
|
"details": {
|
|
98
102
|
"url": "http://localhost:3000/users",
|
|
@@ -111,22 +115,22 @@ Top-level `message`/`messageCode` come from the first failing issue - its `param
|
|
|
111
115
|
}
|
|
112
116
|
```
|
|
113
117
|
|
|
114
|
-
To emit a stable, domain-specific `
|
|
118
|
+
To emit a stable, domain-specific `normalized.code`, attach `params.code` to a custom check:
|
|
115
119
|
|
|
116
120
|
```typescript
|
|
117
121
|
z.string().refine(isEmail, {
|
|
118
122
|
message: 'Invalid email address',
|
|
119
123
|
params: { code: 'user.email.invalid' }
|
|
120
124
|
});
|
|
121
|
-
// produces "
|
|
125
|
+
// produces "normalized": { "code": "user.email.invalid", ... }
|
|
122
126
|
```
|
|
123
127
|
|
|
124
128
|
> [!NOTE]
|
|
125
|
-
> When `error.message` cannot be parsed as the expected Zod issue array (a malformed or unrecognized `ZodError`), no issue-derived
|
|
129
|
+
> When `error.message` cannot be parsed as the expected Zod issue array (a malformed or unrecognized `ZodError`), no issue-derived code exists. `normalized.code` still resolves to `MessageCode.DEFAULT` (`"core.system_error"`) via `MessageCode.resolve(undefined)`. No error response from this middleware is ever missing `normalized.code`.
|
|
126
130
|
|
|
127
131
|
#### 2. PostgreSQL Constraint Violations
|
|
128
132
|
|
|
129
|
-
Database errors in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION violation) are detected by class
|
|
133
|
+
Database errors in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION violation) are detected by class. They return HTTP `400 Bad Request`. A known code uses its specific message; any other in-class code uses `"Invalid database request"` as a fallback.
|
|
130
134
|
|
|
131
135
|
| Class | Codes with a specific message |
|
|
132
136
|
|-------|-------------------------------|
|
|
@@ -135,7 +139,7 @@ Database errors in SQLSTATE class `22` (data exception), `23` (integrity constra
|
|
|
135
139
|
| `44` View check | `44000` WITH CHECK OPTION violation |
|
|
136
140
|
|
|
137
141
|
:::tip Transient conflicts return 409, not 400/500
|
|
138
|
-
Class `40` (`40001` serialization failure, `40P01` deadlock) is transient/retryable
|
|
142
|
+
Class `40` (`40001` serialization failure, `40P01` deadlock) is transient/retryable. It returns **409 Conflict** with `normalized.code: "database.conflict"` and a safe "please retry" message - the client can safely retry the same request. Programming/infra classes (`42` syntax, `53` resources, `0A`, `25`, `28`) remain 500.
|
|
139
143
|
:::
|
|
140
144
|
|
|
141
145
|
:::warning Production sanitizes database internals
|
|
@@ -151,8 +155,12 @@ All other errors use the `statusCode` property from the error if present, otherw
|
|
|
151
155
|
```json
|
|
152
156
|
{
|
|
153
157
|
"message": "Error message",
|
|
154
|
-
"messageCode": "core.system_error",
|
|
155
158
|
"statusCode": 500,
|
|
159
|
+
"normalized": {
|
|
160
|
+
"text": "Error message",
|
|
161
|
+
"code": "core.system_error",
|
|
162
|
+
"args": {}
|
|
163
|
+
},
|
|
156
164
|
"requestId": "abc-123",
|
|
157
165
|
"details": {
|
|
158
166
|
"url": "http://localhost:3000/users",
|
|
@@ -163,14 +171,20 @@ All other errors use the `statusCode` property from the error if present, otherw
|
|
|
163
171
|
}
|
|
164
172
|
```
|
|
165
173
|
|
|
174
|
+
An intentional `getError(...)` throw also carries `extra` when the throw site attached context of its own; every other branch never does. There is no top-level `messageCode` - the code always lives at `normalized.code`.
|
|
175
|
+
|
|
166
176
|
When `rootKey` is provided (e.g., `rootKey: 'error'`), the response is wrapped:
|
|
167
177
|
|
|
168
178
|
```json
|
|
169
179
|
{
|
|
170
180
|
"error": {
|
|
171
181
|
"message": "Error message",
|
|
172
|
-
"messageCode": "core.system_error",
|
|
173
182
|
"statusCode": 500,
|
|
183
|
+
"normalized": {
|
|
184
|
+
"text": "Error message",
|
|
185
|
+
"code": "core.system_error",
|
|
186
|
+
"args": {}
|
|
187
|
+
},
|
|
174
188
|
"requestId": "abc-123",
|
|
175
189
|
"details": { ... }
|
|
176
190
|
}
|
|
@@ -215,13 +229,13 @@ Returns a JSON 404 response when no route matches. Registered via `server.notFou
|
|
|
215
229
|
|
|
216
230
|
```typescript
|
|
217
231
|
function notFoundHandler(opts: {
|
|
218
|
-
logger?:
|
|
232
|
+
logger?: ILogger;
|
|
219
233
|
}): NotFoundHandler
|
|
220
234
|
```
|
|
221
235
|
|
|
222
236
|
| Parameter | Type | Description |
|
|
223
237
|
|-----------|------|-------------|
|
|
224
|
-
| `logger` | `
|
|
238
|
+
| `logger` | `ILogger \| undefined` | Logger instance (defaults to `console`) |
|
|
225
239
|
|
|
226
240
|
### Response Format
|
|
227
241
|
|
|
@@ -235,7 +249,8 @@ function notFoundHandler(opts: {
|
|
|
235
249
|
}
|
|
236
250
|
```
|
|
237
251
|
|
|
238
|
-
The handler logs the 404 at
|
|
252
|
+
The handler logs the 404 at warn level with the request ID, path, and full URL. An unrouted path is a
|
|
253
|
+
client mistake, not a server fault - alerting tuned to error level should not fire on it.
|
|
239
254
|
|
|
240
255
|
|
|
241
256
|
## RequestSpyMiddleware
|
|
@@ -363,7 +378,7 @@ Several middleware behaviors are configured through `IApplicationConfigs`:
|
|
|
363
378
|
```typescript
|
|
364
379
|
interface IApplicationConfigs {
|
|
365
380
|
favicon?: string; // Emoji for emojiFavicon (default: '🔥')
|
|
366
|
-
error?: { rootKey: string }; // Root key wrapper for
|
|
381
|
+
error?: { rootKey: string }; // Root key wrapper for AppErrorMiddleware
|
|
367
382
|
asyncContext?: { enable: boolean }; // Enable Hono contextStorage() middleware
|
|
368
383
|
// ...
|
|
369
384
|
}
|
|
@@ -482,7 +497,7 @@ export class MyMiddleware extends BaseHelper implements IProvider<MiddlewareHand
|
|
|
482
497
|
}
|
|
483
498
|
```
|
|
484
499
|
|
|
485
|
-
Register it with `.toProvider()` (the same pattern `RequestTrackerComponent` uses for `RequestSpyMiddleware`)
|
|
500
|
+
Register it with `.toProvider()` (the same pattern `RequestTrackerComponent` uses for `RequestSpyMiddleware`). Then use the resolved handler inside `setupMiddlewares()`: `get()` returns the produced `MiddlewareHandler`, because the container calls `value()` for provider bindings.
|
|
486
501
|
|
|
487
502
|
```typescript
|
|
488
503
|
export class MyApplication extends BaseApplication {
|