@venizia/ignis-docs 0.2.1-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.
Files changed (145) hide show
  1. package/content/best-practices/architectural-patterns.md +3 -3
  2. package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
  3. package/content/best-practices/contribution-workflow.md +2 -2
  4. package/content/best-practices/error-handling.md +94 -89
  5. package/content/best-practices/security-guidelines.md +5 -5
  6. package/content/extensions/components/api-reference.md +22 -21
  7. package/content/extensions/components/authentication/api.md +64 -28
  8. package/content/extensions/components/authentication/errors.md +19 -5
  9. package/content/extensions/components/authentication/index.md +19 -18
  10. package/content/extensions/components/authentication/usage.md +55 -30
  11. package/content/extensions/components/authorization/api.md +351 -84
  12. package/content/extensions/components/authorization/errors.md +51 -17
  13. package/content/extensions/components/authorization/getting-started.md +227 -0
  14. package/content/extensions/components/authorization/index.md +24 -16
  15. package/content/extensions/components/authorization/usage.md +45 -21
  16. package/content/extensions/components/health-check.md +15 -9
  17. package/content/extensions/components/index.md +24 -90
  18. package/content/extensions/components/mail/api.md +105 -54
  19. package/content/extensions/components/mail/errors.md +12 -10
  20. package/content/extensions/components/mail/index.md +32 -13
  21. package/content/extensions/components/mail/usage.md +26 -18
  22. package/content/extensions/components/request-tracker.md +18 -14
  23. package/content/extensions/components/socket-io/api.md +377 -882
  24. package/content/extensions/components/socket-io/errors.md +49 -51
  25. package/content/extensions/components/socket-io/index.md +72 -88
  26. package/content/extensions/components/socket-io/usage.md +107 -117
  27. package/content/extensions/components/static-asset/api.md +83 -31
  28. package/content/extensions/components/static-asset/errors.md +18 -7
  29. package/content/extensions/components/static-asset/index.md +18 -11
  30. package/content/extensions/components/static-asset/usage.md +11 -6
  31. package/content/extensions/components/template/index.md +3 -3
  32. package/content/extensions/components/websocket/api.md +58 -27
  33. package/content/extensions/components/websocket/errors.md +3 -3
  34. package/content/extensions/components/websocket/index.md +8 -7
  35. package/content/extensions/components/websocket/usage.md +21 -8
  36. package/content/extensions/helpers/cron/index.md +8 -7
  37. package/content/extensions/helpers/crypto/index.md +16 -8
  38. package/content/extensions/helpers/crypto/reference.md +96 -24
  39. package/content/extensions/helpers/env/index.md +14 -10
  40. package/content/extensions/helpers/error/index.md +99 -23
  41. package/content/extensions/helpers/index.md +61 -47
  42. package/content/extensions/helpers/inversion/index.md +23 -6
  43. package/content/extensions/helpers/inversion/reference.md +30 -22
  44. package/content/extensions/helpers/kafka/admin.md +3 -2
  45. package/content/extensions/helpers/kafka/compile-binary.md +69 -44
  46. package/content/extensions/helpers/kafka/consumer.md +24 -21
  47. package/content/extensions/helpers/kafka/examples.md +22 -234
  48. package/content/extensions/helpers/kafka/index.md +30 -62
  49. package/content/extensions/helpers/kafka/producer.md +31 -26
  50. package/content/extensions/helpers/kafka/schema-registry.md +19 -14
  51. package/content/extensions/helpers/logger/hf-logger.md +49 -22
  52. package/content/extensions/helpers/logger/index.md +37 -12
  53. package/content/extensions/helpers/logger/pino.md +31 -11
  54. package/content/extensions/helpers/logger/reference.md +243 -52
  55. package/content/extensions/helpers/network/api.md +65 -30
  56. package/content/extensions/helpers/network/index.md +32 -11
  57. package/content/extensions/helpers/queue/index.md +17 -6
  58. package/content/extensions/helpers/queue/reference.md +52 -25
  59. package/content/extensions/helpers/redis/index.md +29 -11
  60. package/content/extensions/helpers/redis/reference.md +85 -28
  61. package/content/extensions/helpers/secrets/index.md +82 -12
  62. package/content/extensions/helpers/socket-io/api.md +40 -21
  63. package/content/extensions/helpers/socket-io/index.md +26 -10
  64. package/content/extensions/helpers/storage/api.md +42 -24
  65. package/content/extensions/helpers/storage/index.md +10 -7
  66. package/content/extensions/helpers/types/index.md +20 -7
  67. package/content/extensions/helpers/types/reference.md +53 -14
  68. package/content/extensions/helpers/uid/index.md +166 -10
  69. package/content/extensions/helpers/websocket/api.md +52 -13
  70. package/content/extensions/helpers/websocket/index.md +7 -4
  71. package/content/extensions/helpers/worker-thread/index.md +9 -5
  72. package/content/extensions/helpers/worker-thread/reference.md +20 -20
  73. package/content/extensions/index.md +38 -39
  74. package/content/extensions/src-details/mcp-server.md +96 -548
  75. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  76. package/content/guides/core-concepts/persistent/index.md +5 -1
  77. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  78. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  79. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  80. package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
  81. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  82. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  83. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  84. package/content/guides/get-started/philosophy.md +135 -670
  85. package/content/guides/get-started/setup.md +53 -74
  86. package/content/guides/migrations/redis-helpers-migration.md +4 -3
  87. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  88. package/content/guides/migrations/unified-connectors-migration.md +7 -6
  89. package/content/guides/tutorials/realtime-chat.md +1 -1
  90. package/content/references/base/application.md +63 -21
  91. package/content/references/base/components.md +3 -3
  92. package/content/references/base/connectors.md +14 -14
  93. package/content/references/base/controllers.md +12 -12
  94. package/content/references/base/datasources-reference.md +32 -31
  95. package/content/references/base/datasources.md +6 -6
  96. package/content/references/base/dependency-injection.md +12 -11
  97. package/content/references/base/filter-system/application-usage.md +54 -30
  98. package/content/references/base/filter-system/array-operators.md +24 -46
  99. package/content/references/base/filter-system/comparison-operators.md +47 -67
  100. package/content/references/base/filter-system/default-filter.md +59 -53
  101. package/content/references/base/filter-system/fields-order-pagination.md +92 -146
  102. package/content/references/base/filter-system/index.md +25 -13
  103. package/content/references/base/filter-system/json-filtering.md +45 -184
  104. package/content/references/base/filter-system/list-operators.md +23 -53
  105. package/content/references/base/filter-system/logical-operators.md +63 -121
  106. package/content/references/base/filter-system/null-operators.md +34 -104
  107. package/content/references/base/filter-system/pattern-matching.md +40 -55
  108. package/content/references/base/filter-system/quick-reference.md +86 -198
  109. package/content/references/base/filter-system/range-operators.md +18 -46
  110. package/content/references/base/filter-system/tips.md +6 -6
  111. package/content/references/base/filter-system/use-cases.md +33 -15
  112. package/content/references/base/grpc-controllers.md +53 -17
  113. package/content/references/base/index.md +5 -3
  114. package/content/references/base/middlewares.md +11 -10
  115. package/content/references/base/models-reference.md +17 -17
  116. package/content/references/base/models.md +4 -3
  117. package/content/references/base/providers.md +8 -8
  118. package/content/references/base/repositories/advanced.md +224 -326
  119. package/content/references/base/repositories/index.md +19 -6
  120. package/content/references/base/repositories/mixins.md +5 -5
  121. package/content/references/base/repositories/relations.md +160 -293
  122. package/content/references/base/repositories/soft-deletable.md +16 -6
  123. package/content/references/base/secrets.md +17 -13
  124. package/content/references/base/services.md +6 -4
  125. package/content/references/configuration/environment-variables.md +31 -23
  126. package/content/references/configuration/index.md +6 -4
  127. package/content/references/index.md +1 -1
  128. package/content/references/utilities/duration.md +85 -0
  129. package/content/references/utilities/index.md +5 -1
  130. package/content/references/utilities/jsx-reference.md +11 -11
  131. package/content/references/utilities/jsx.md +2 -2
  132. package/content/references/utilities/module.md +78 -25
  133. package/content/references/utilities/request.md +2 -1
  134. package/content/references/utilities/retry.md +139 -0
  135. package/content/references/utilities/schema.md +2 -2
  136. package/content/references/utilities/statuses-reference.md +4 -4
  137. package/content/references/utilities/statuses.md +5 -5
  138. package/dist/mcp-server/index.js +0 -0
  139. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  140. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  141. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  142. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  143. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  144. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  145. package/package.json +17 -16
@@ -1,45 +1,16 @@
1
1
  ---
2
2
  title: Kafka - Compiling to a Single Binary
3
- description: The platformaticWasmPlugin() Bun bundler plugin required when compiling an app that imports a Kafka helper
3
+ description: The platformaticKafkaPlugins() Bun bundler plugins required when compiling an app that imports a Kafka helper
4
4
  difficulty: advanced
5
5
  ---
6
6
 
7
7
  # Compiling to a Single Binary
8
8
 
9
- This page covers `platformaticWasmPlugin()`, the Bun bundler plugin required when compiling an application that imports any Kafka helper into a standalone `bun build --compile` executable.
10
-
11
- ## The problem
12
-
13
- - **A compiled binary crashes on startup** if the application imports any Kafka helper and the build did not register the plugin:
14
-
15
- ```
16
- ENOENT: no such file or directory, open '/$bunfs/dist/native.wasm'
17
- ```
18
-
19
- - **The crash happens before your code runs.** It fires while the module graph is still loading - before the IGNIS application boots, so no log line, no lifecycle hook, and no error handler of yours ever executes.
20
-
21
- ## Why it happens
22
-
23
- - **The codecs are WebAssembly, not native bindings.** `@platformatic/kafka` computes Kafka's CRC32C checksums and lz4/snappy compression in WebAssembly, through `@platformatic/wasm-utils`.
24
- - **The default entrypoint reads the wasm payload from disk** at module load time:
25
-
26
- ```javascript
27
- // @platformatic/wasm-utils/dist/index.js
28
- const wasm = readFileSync(new URL('../dist/native.wasm', import.meta.url));
29
- ```
30
-
31
- - **`bun build --compile` embeds JavaScript modules only.** Assets such as `native.wasm` are never carried into the executable. Inside the binary, `import.meta.url` resolves against the virtual `/$bunfs` filesystem, the file isn't there, and the read throws.
32
- - **Running from source is unaffected.** `bun run` / `bun .` keep `node_modules` on disk, so the read succeeds - the bug only exists in compiled binaries.
33
-
34
- ## The fix
35
-
36
- - **A second entrypoint ships the payload inline.** `@platformatic/wasm-utils/bundled` exposes the same API with the wasm payload inlined as base64 - no filesystem read.
37
- - **`platformaticWasmPlugin()` swaps one entrypoint for the other** at bundle time, so the rest of your code and `@platformatic/kafka` itself need no changes.
38
- - **Compile through a Bun build script, not the CLI.** `bun build --compile` on the command line cannot register plugins:
9
+ `platformaticKafkaPlugins()` returns every Bun bundler plugin `@platformatic/kafka` needs to survive `bun build --compile`. Register them whenever you compile an application that imports a Kafka helper. Skip them, and the binary crashes on its first run.
39
10
 
40
11
  ```typescript
41
12
  // scripts/compile.ts
42
- import { platformaticWasmPlugin } from '@venizia/ignis-helpers/kafka';
13
+ import { platformaticKafkaPlugins } from '@venizia/ignis-helpers/kafka';
43
14
 
44
15
  const built = await Bun.build({
45
16
  entrypoints: ['./dist/index.js'],
@@ -50,7 +21,7 @@ const built = await Bun.build({
50
21
  target: process.env.BUN_TARGET ?? 'bun-linux-x64',
51
22
  outfile: './dist/bin',
52
23
  },
53
- plugins: [platformaticWasmPlugin()],
24
+ plugins: platformaticKafkaPlugins(),
54
25
  });
55
26
 
56
27
  if (!built.success) {
@@ -59,6 +30,8 @@ if (!built.success) {
59
30
  }
60
31
  ```
61
32
 
33
+ `bun build --compile` on the command line cannot register plugins. Compile through a build script instead:
34
+
62
35
  ```json
63
36
  {
64
37
  "scripts": {
@@ -67,32 +40,84 @@ if (!built.success) {
67
40
  }
68
41
  ```
69
42
 
70
- - **The plugin resolves relative to the importing module's own directory** (`Bun.resolveSync` from `dirname(args.importer)`), so it works with hoisted and isolated `node_modules` layouts alike, and pins no package version.
43
+ ## The two crashes
71
44
 
72
- ## Verifying
45
+ | Crash | Plugin that prevents it |
46
+ |---|---|
47
+ | `ENOENT: no such file or directory, open '/$bunfs/dist/native.wasm'` | `platformaticWasmPlugin()` |
48
+ | `Cannot find package 'ajv-draft-04' from '/$bunfs/root/index.js'` | `platformaticRequirePlugin()` |
49
+
50
+ Both fire while the module graph is still loading, before the IGNIS application boots. No log line, no lifecycle hook, and no error handler of yours ever runs.
51
+
52
+ Running from source is unaffected. `bun run` and `bun .` keep `node_modules` on disk, so both reads succeed. The crashes exist only in compiled binaries.
53
+
54
+ ## Why the wasm read fails
73
55
 
74
- A correctly built binary contains no reference to the wasm file on disk:
56
+ `@platformatic/kafka` computes Kafka's CRC32C checksums and lz4/snappy compression in WebAssembly, through `@platformatic/wasm-utils`. That package reads its payload from disk at module load:
57
+
58
+ ```javascript
59
+ // @platformatic/wasm-utils/dist/index.js
60
+ const wasm = readFileSync(new URL('../dist/native.wasm', import.meta.url));
61
+ ```
62
+
63
+ `bun build --compile` embeds JavaScript modules only. Assets such as `native.wasm` never reach the executable, so `import.meta.url` resolves against the virtual `/$bunfs` filesystem and the read throws.
64
+
65
+ `platformaticWasmPlugin()` redirects the import to `@platformatic/wasm-utils/bundled`, the same API with the payload inlined as base64.
66
+
67
+ ## Why the package lookup fails
68
+
69
+ Since 2.8.0, `@platformatic/kafka` resolves two dependencies at module scope through `createRequire`:
70
+
71
+ ```javascript
72
+ // @platformatic/kafka/dist/registries/confluent-schema-registry.js
73
+ const require = createRequire(import.meta.url);
74
+ const AjvDraft04 = require('ajv-draft-04');
75
+ const draft06MetaSchema = require('ajv/dist/refs/json-schema-draft-06.json');
76
+ ```
77
+
78
+ Three facts make that fatal in a binary:
79
+
80
+ - **The bundler cannot see through `createRequire`.** Neither module is embedded, and the bare specifier survives verbatim into the binary.
81
+ - **The module always loads.** `dist/index.js` re-exports the schema registry, so importing anything from the package runs those two lines. A schema registry you never use still crashes the binary.
82
+ - **A compiled binary has no `node_modules`.** `import.meta.url` resolves against `/$bunfs` and the lookup fails.
83
+
84
+ `platformaticRequirePlugin()` rewrites each module-scope `require()` into a static import at bundle time, so the bundler pulls the module into the binary.
85
+
86
+ > [!WARNING]
87
+ > Installing `ajv-draft-04` as a direct dependency does not fix this. The defect is the resolution mechanism, not a missing package, so the second line fails identically.
88
+
89
+ ## Verifying
75
90
 
76
91
  ```bash
77
- grep -c 'native.wasm' ./dist/bin # 0 -- the payload is inlined
78
- ./dist/bin # boots instead of throwing ENOENT
92
+ ./dist/bin # boots instead of crashing
93
+ grep -ac 'native.wasm' ./dist/bin # 0 - the wasm payload is inlined
94
+ grep -ac 'ajv-draft-04' ./dist/bin # 0 - the package is bundled in
79
95
  ```
80
96
 
81
- The binary grows by roughly 76 KB, the base64 form of the 57 KB wasm module.
97
+ The last count is meaningful only for a minified build. An unminified bundle keeps package paths in module comments.
98
+
99
+ The binary grows by roughly 76 KB for the base64 wasm module, plus the size of the two hoisted packages.
82
100
 
83
101
  ## Notes
84
102
 
85
- - **Upgrading `@platformatic/kafka` does not remove the need for the plugin.** Every release to date imports the default `@platformatic/wasm-utils` entrypoint.
86
- - **Apps that never import a Kafka helper need no plugin.** Nothing pulls in `@platformatic/wasm-utils`, and the plugin's resolver never fires.
87
- - **Patching `node_modules` during the build achieves the same result**, but mutates a dependency in place, pins the store path to one version, and leaves the tree dirty when a build fails. The plugin needs neither.
103
+ - **Register the pair, not the individual plugins.** `platformaticKafkaPlugins()` picks up any future addition without another edit to your compile script. `platformaticWasmPlugin()` and `platformaticRequirePlugin()` stay exported for a build that needs one alone.
104
+ - **Upgrading `@platformatic/kafka` does not remove the need for the plugins.** Every release to date imports the default `@platformatic/wasm-utils` entrypoint, and 2.8.0 introduced the `createRequire` calls in a minor release.
105
+ - **An unresolvable specifier now fails the build**, with Bun's own resolution error and `built.success === false`. That is the point of hoisting to a static import: you learn at build time instead of on the first run in production.
106
+ - **Both plugins resolve relative to the importing module's own directory.** That works with hoisted and isolated `node_modules` layouts alike, and pins no package version.
107
+ - **Apps that never import a Kafka helper need no plugins.** Nothing pulls in `@platformatic/wasm-utils` or the schema registry, and neither plugin ever fires.
108
+ - **`platformaticRequirePlugin()` skips `protobufjs` and `@node-rs/crc32`.** Both are optional peers of `@platformatic/kafka`, and a static import would break builds that do not install them.
109
+ - **Patching `node_modules` during the build achieves the same result, but at a cost.** It mutates a dependency in place, pins the store path to one version, and leaves the tree dirty when a build fails. The plugins need none of that.
88
110
 
89
111
  ## See also
90
112
 
91
- - [Kafka Overview](./) - the four helpers this plugin protects
113
+ - [Kafka Overview](./) - the four helpers these plugins protect
92
114
  - [Producer](./producer) / [Consumer](./consumer) / [Admin](./admin) - any of these importing into a compiled binary triggers this caveat
115
+ - [Schema Registry](./schema-registry) - the helper that wraps the module the second crash comes from
93
116
  - [Examples & Troubleshooting](./examples) - other Kafka connection and configuration errors
94
117
 
95
118
  **Files:**
96
119
 
120
+ - [`packages/helpers/src/modules/queue/kafka/bundler/plugins.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/bundler/plugins.ts) - `platformaticKafkaPlugins`
97
121
  - [`packages/helpers/src/modules/queue/kafka/bundler/platformatic-wasm.plugin.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/bundler/platformatic-wasm.plugin.ts) - `platformaticWasmPlugin`
98
- - [`packages/helpers/src/modules/queue/kafka/bundler/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/bundler/common/constants.ts) - `KafkaBundlerPluginNames`, `PlatformaticWasmSpecifiers`
122
+ - [`packages/helpers/src/modules/queue/kafka/bundler/platformatic-require.plugin.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/bundler/platformatic-require.plugin.ts) - `platformaticRequirePlugin`
123
+ - [`packages/helpers/src/modules/queue/kafka/bundler/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/bundler/common/constants.ts) - `KafkaBundlerPluginNames`, `PlatformaticWasmSpecifiers`, `PlatformaticRequireSpecifiers`
@@ -6,7 +6,7 @@ difficulty: intermediate
6
6
 
7
7
  # Consumer
8
8
 
9
- The `KafkaConsumerHelper` wraps `@platformatic/kafka`'s `Consumer` with health tracking, graceful shutdown, message callbacks, consumer group event callbacks, automatic reconnect, and lag monitoring.
9
+ The `KafkaConsumerHelper` wraps `@platformatic/kafka`'s `Consumer` with health tracking, graceful shutdown, message callbacks, group callbacks, automatic reconnect, and lag monitoring.
10
10
 
11
11
  ```typescript
12
12
  class KafkaConsumerHelper<
@@ -46,12 +46,12 @@ Plus the shared [Connection & Authentication](./producer#connection--authenticat
46
46
 
47
47
  | Option | Type | Default | Description |
48
48
  |--------|------|---------|-------------|
49
- | `groupId` | `string` | -- | Consumer group ID. **Required** |
49
+ | `groupId` | `string` | - | Consumer group ID. **Required** |
50
50
  | `identifier` | `string` | `'kafka-consumer'` | Scoped logging identifier |
51
- | `deserializers` | `Partial<Deserializers<K,V,HK,HV>>` | -- | Key/value/header deserializers |
51
+ | `deserializers` | `Partial<Deserializers<K,V,HK,HV>>` | - | Key/value/header deserializers |
52
52
  | `autocommit` | `boolean \| number` | `false` | Auto-commit offsets. `true` = default interval, `number` = custom ms |
53
- | `sessionTimeout` | `number` | `60000` | Session timeout -- consumer removed from group if no heartbeat |
54
- | `heartbeatInterval` | `number` | `10000` | Heartbeat interval -- must be less than `sessionTimeout` |
53
+ | `sessionTimeout` | `number` | `60000` | Session timeout - consumer removed from group if no heartbeat |
54
+ | `heartbeatInterval` | `number` | `10000` | Heartbeat interval - must be less than `sessionTimeout` |
55
55
  | `rebalanceTimeout` | `number` | `sessionTimeout` | Max time for rebalance. Defaults to the value of `sessionTimeout` |
56
56
  | `highWaterMark` | `number` | `1024` | Stream buffer size (messages) |
57
57
  | `minBytes` | `number` | `1` | Min bytes per fetch response |
@@ -59,9 +59,9 @@ Plus the shared [Connection & Authentication](./producer#connection--authenticat
59
59
  | `maxWaitTime` | `number` | `5000` | Max time (ms) broker waits for `minBytes` |
60
60
  | `metadataMaxAge` | `number` | `300000` | Metadata cache TTL (ms) |
61
61
  | `groupProtocol` | `'classic' \| 'consumer'` | `'classic'` | Consumer group protocol. `'consumer'` = KIP-848 (Kafka 3.7+) |
62
- | `groupInstanceId` | `string` | -- | Static group membership ID -- prevents rebalance on restart |
62
+ | `groupInstanceId` | `string` | - | Static group membership ID - prevents rebalance on restart |
63
63
  | `shutdownTimeout` | `number` | `30000` | Graceful shutdown timeout in ms |
64
- | `registry` | `SchemaRegistry` | -- | Schema registry for auto deser |
64
+ | `registry` | `SchemaRegistry` | - | Schema registry for auto deser |
65
65
 
66
66
  ### Lifecycle Callbacks
67
67
 
@@ -169,14 +169,15 @@ Stream 'error' event
169
169
  -> onMessageError({ error }) (no message available)
170
170
  ```
171
171
 
172
- - **`onMessage` is the main processing callback** -- do your business logic here.
173
- - **`onMessageDone` fires only after `onMessage` resolves successfully** -- use it for logging, metrics, and similar side effects. Note that errors thrown from `onMessageDone` also trigger `onMessageError`.
174
- - **`onMessageError` fires if `onMessage` throws**, and also for the stream's own `'error'` event (without a `message`, since it's a stream-level error).
175
- - **The stream `'error'` listener is always attached**, whether or not you pass `onMessageError`. An `EventEmitter` `'error'` event with zero listeners is rethrown as an uncaught exception -- without this listener, a pull-style consumer (`start()` + `getStream()`, no `onMessage`) would take the whole process down on the first broker drop.
172
+ - **`onMessage` is the main processing callback.** Put your business logic here.
173
+ - **`onMessageDone` fires only after `onMessage` resolves successfully.** Use it for logging, metrics, and similar side effects. An error thrown from `onMessageDone` also triggers `onMessageError`.
174
+ - **`onMessageError` fires if `onMessage` throws.** It also fires for the stream's own `'error'` event. That case carries no `message` - it's a stream-level error, not a per-message one.
175
+ - **The stream `'error'` listener is always attached**, whether or not you pass `onMessageError`. An `EventEmitter` `'error'` event with zero listeners becomes an uncaught exception.
176
+ - **Pull-style consumers benefit too.** Without this listener, a consumer using `start()` + `getStream()` with no `onMessage` would take down the whole process on the first broker drop.
176
177
 
177
178
  ## start()
178
179
 
179
- `start()` creates the consume stream and wires all message callbacks. It must be called explicitly after construction, and it guards against duplicate starts -- calling it twice logs a warning and returns immediately.
180
+ `start()` creates the consume stream and wires all message callbacks. Call it explicitly after construction - the constructor never calls it for you. It also guards against duplicate starts: calling it twice logs a warning and returns immediately.
180
181
 
181
182
  ```typescript
182
183
  interface IKafkaConsumeStartOptions {
@@ -197,8 +198,8 @@ interface IKafkaConsumeStartOptions {
197
198
 
198
199
  | Fallback | Description |
199
200
  |----------|-------------|
200
- | `'latest'` (default) | Start from latest -- ignore historical messages |
201
- | `'earliest'` | Start from beginning -- process all historical messages |
201
+ | `'latest'` (default) | Start from latest - ignore historical messages |
202
+ | `'earliest'` | Start from beginning - process all historical messages |
202
203
  | `'fail'` | Throw an error |
203
204
 
204
205
  | Reconnect option | Default | Description |
@@ -227,11 +228,13 @@ await helper.start({
227
228
 
228
229
  When `onMessage` is provided, `start()` drives a background consume loop on top of the stream. That loop reconnects on its own:
229
230
 
230
- - **Only the callback-driven loop reconnects.** A pull-style consumer using `getStream()` or `consumer.consume()` directly owns its own retry logic -- the helper's automatic reconnect only runs inside `startConsumeLoop`, which is wired exclusively when `onMessage` is set.
231
- - **A full broker outage marks the session stale.** When every broker disconnects (`getConnectedBrokerCount()` drops to `0`, via `client:broker:disconnect` or `client:broker:failed`), the helper destroys the current stream immediately instead of waiting for it to error out on its own.
232
- - **Reconnect rebuilds the client after a stale session.** The next attempt, after `reconnectDelayMs`, constructs a brand-new `@platformatic/kafka` `Consumer` with the original constructor options and swaps it in -- a fresh client forces a clean group rejoin rather than reusing session state Kafka has likely already expired. Active lag monitoring is re-armed on the new client automatically.
231
+ - **Only the callback-driven loop reconnects.** A pull-style consumer using `getStream()` or `consumer.consume()` directly owns its own retry logic. The helper's automatic reconnect runs only inside `startConsumeLoop`, which is wired exclusively when `onMessage` is set.
232
+ - **A full broker outage marks the session stale.** This happens when every broker disconnects - `getConnectedBrokerCount()` drops to `0` via `client:broker:disconnect` or `client:broker:failed` events.
233
+ - **The helper reacts immediately.** It destroys the current stream right away, instead of waiting for the stream to error out on its own.
234
+ - **Reconnect rebuilds the client after a stale session.** The next attempt, after `reconnectDelayMs`, constructs a brand-new `@platformatic/kafka` `Consumer` with the original options and swaps it in.
235
+ - **A fresh client forces a clean group rejoin**, instead of reusing session state Kafka has likely already expired. Lag monitoring re-arms automatically on the new client.
233
236
  - **Attempts are capped.** After `maxReconnectAttempts` consecutive failures, the consume loop exits and logs an error. Message processing stops until you call `start()` again with a new set of options.
234
- - **Every retry is logged**, including the attempt number, delay, and current connected-broker count, so a stuck reconnect loop is visible in application logs without extra instrumentation.
237
+ - **Every retry is logged**: the attempt number, the delay, and the current connected-broker count. A stuck reconnect loop stays visible in application logs without extra instrumentation.
235
238
 
236
239
  ## Lag Monitoring
237
240
 
@@ -243,7 +246,7 @@ helper.startLagMonitoring({ topics: ['orders'], interval: 10_000 });
243
246
  helper.stopLagMonitoring();
244
247
  ```
245
248
 
246
- Lag data is delivered via the `onLag` callback. Errors via `onLagError`. `interval` defaults to `30000` ms. Guards against duplicate starts -- calling `startLagMonitoring()` twice logs a warning.
249
+ Lag data arrives through the `onLag` callback; errors through `onLagError`. `interval` defaults to `30000` ms. Calling `startLagMonitoring()` twice logs a warning instead of starting a second poll loop.
247
250
 
248
251
  For one-time lag checks, use the underlying consumer directly:
249
252
 
@@ -378,7 +381,7 @@ consumer.generationId; // number
378
381
  consumer.assignments; // GroupAssignment[] | null
379
382
  consumer.isActive(); // boolean
380
383
 
381
- // Static membership -- prevents rebalance on restart
384
+ // Static membership - prevents rebalance on restart
382
385
  const helper = KafkaConsumerHelper.newInstance({
383
386
  ...
384
387
  groupInstanceId: 'worker-1',
@@ -410,7 +413,7 @@ Topic "orders" (3 partitions)
410
413
  - [Kafka Overview](./) - the four helpers, shared health/close API, and the compile-binary caveat
411
414
  - [Producer](./producer) - the sending side, plus the shared Connection & Authentication options
412
415
  - [Admin](./admin) - create topics and inspect consumer groups from outside the running consumer
413
- - [Examples & Troubleshooting](./examples) - lag monitoring, exactly-once, and common connection errors
416
+ - [Examples & Troubleshooting](./examples) - IoC wiring and the common connection-error lookup table
414
417
 
415
418
  **Files:**
416
419
 
@@ -1,140 +1,17 @@
1
1
  ---
2
2
  title: Kafka Examples & Troubleshooting
3
- description: End-to-end producer, consumer, admin, and IoC examples, plus a common-error lookup table
3
+ description: A multi-topic admin setup script, wiring Kafka helpers into IGNIS IoC, and a common-error lookup table
4
4
  difficulty: intermediate
5
5
  ---
6
6
 
7
7
  # Examples & Troubleshooting
8
8
 
9
- Complete examples and common issue resolution for the Kafka helpers.
10
-
11
- ## Producer: Send Messages with Health Monitoring
12
-
13
- ```typescript
14
- import { KafkaProducerHelper, KafkaAcks } from '@venizia/ignis-helpers/kafka';
15
- import { stringSerializers } from '@platformatic/kafka';
16
-
17
- const helper = KafkaProducerHelper.newInstance({
18
- bootstrapBrokers: ['broker1:9092', 'broker2:9092'],
19
- clientId: 'interval-producer',
20
- serializers: stringSerializers,
21
- acks: KafkaAcks.ALL,
22
- onBrokerConnect: ({ broker }) => console.log(`Connected to ${broker.host}:${broker.port}`),
23
- onBrokerDisconnect: ({ broker }) => console.warn(`Disconnected from ${broker.host}`),
24
- });
25
-
26
- const producer = helper.getProducer();
27
- let count = 0;
28
-
29
- const interval = setInterval(async () => {
30
- if (!helper.isHealthy()) {
31
- console.warn('Producer not healthy, skipping...');
32
- return;
33
- }
34
-
35
- await producer.send({
36
- messages: [{
37
- topic: 'events',
38
- key: `key-${count % 3}`,
39
- value: JSON.stringify({ index: count, timestamp: new Date().toISOString() }),
40
- }],
41
- });
42
- count++;
43
- }, 100);
44
-
45
- process.on('SIGINT', async () => {
46
- clearInterval(interval);
47
- console.log(`Shutting down... (sent ${count} messages)`);
48
- await helper.close();
49
- process.exit(0);
50
- });
51
- ```
52
-
53
- ## Consumer: Callback-Based with Lag Monitoring
54
-
55
- ```typescript
56
- import { KafkaConsumerHelper } from '@venizia/ignis-helpers/kafka';
57
- import { stringDeserializers } from '@platformatic/kafka';
58
-
59
- const helper = KafkaConsumerHelper.newInstance({
60
- bootstrapBrokers: ['broker1:9092', 'broker2:9092'],
61
- clientId: 'event-consumer',
62
- groupId: 'processing-group',
63
- deserializers: stringDeserializers,
64
-
65
- onMessage: async ({ message }) => {
66
- const data = JSON.parse(message.value!);
67
- console.log(`Processing: ${message.key} -> ${JSON.stringify(data)}`);
68
- await message.commit();
69
- },
70
- onMessageDone: ({ message }) => {
71
- console.log(`Done: ${message.key}`);
72
- },
73
- onMessageError: ({ error, message }) => {
74
- console.error(`Error processing ${message?.key}:`, error.message);
75
- },
76
-
77
- onGroupJoin: ({ groupId, memberId }) => {
78
- console.log(`Joined group ${groupId} as ${memberId}`);
79
- },
80
- onGroupRebalance: ({ groupId }) => {
81
- console.log(`Rebalance in ${groupId}`);
82
- },
83
-
84
- onLag: ({ lag }) => {
85
- for (const [topic, partitionLags] of lag) {
86
- partitionLags.forEach((lagValue, partition) => {
87
- if (lagValue > 1000n) {
88
- console.warn(`High lag on ${topic}[${partition}]: ${lagValue}`);
89
- }
90
- });
91
- }
92
- },
93
- onLagError: ({ error }) => console.error('Lag error:', error),
94
- });
95
-
96
- await helper.start({ topics: ['events'] });
97
- helper.startLagMonitoring({ topics: ['events'], interval: 10_000 });
98
-
99
- process.on('SIGINT', async () => {
100
- await helper.close();
101
- process.exit(0);
102
- });
103
- ```
104
-
105
- ## Consumer: Direct Stream Access (for-await)
106
-
107
- ```typescript
108
- import { KafkaConsumerHelper } from '@venizia/ignis-helpers/kafka';
109
- import { stringDeserializers } from '@platformatic/kafka';
110
-
111
- const helper = KafkaConsumerHelper.newInstance({
112
- bootstrapBrokers: ['localhost:9092'],
113
- clientId: 'stream-consumer',
114
- groupId: 'stream-group',
115
- deserializers: stringDeserializers,
116
- onBrokerConnect: ({ broker }) => console.log(`Connected to ${broker.host}`),
117
- });
118
-
119
- // Use the consumer directly for async iterator pattern
120
- const consumer = helper.getConsumer();
121
- const stream = await consumer.consume({
122
- topics: ['orders'],
123
- mode: 'committed',
124
- fallbackMode: 'latest',
125
- });
126
-
127
- for await (const message of stream) {
128
- console.log(`${message.topic}[${message.partition}] @${message.offset}: ${message.key} -> ${message.value}`);
129
- await message.commit();
130
- }
131
-
132
- await stream.close();
133
- await helper.close();
134
- ```
9
+ Two examples not covered on the reference pages, plus a lookup table for the errors you'll actually hit.
135
10
 
136
11
  ## Admin: Topic Setup Script
137
12
 
13
+ A deploy-time script that creates several topics with production configuration in one call. Run it once, from a `migrate` step or a CI job.
14
+
138
15
  ```typescript
139
16
  import { KafkaAdminHelper } from '@venizia/ignis-helpers/kafka';
140
17
 
@@ -147,7 +24,6 @@ async function setupTopics() {
147
24
 
148
25
  const admin = helper.getAdmin();
149
26
 
150
- // Create topics
151
27
  await admin.createTopics({
152
28
  topics: ['orders', 'inventory', 'notifications'],
153
29
  partitions: 6,
@@ -158,11 +34,8 @@ async function setupTopics() {
158
34
  ],
159
35
  });
160
36
 
161
- // Verify
162
37
  const topics = await admin.listTopics({ includeInternals: false });
163
38
  console.log('Topics:', topics);
164
-
165
- // Health check
166
39
  console.log('Healthy:', helper.isHealthy());
167
40
 
168
41
  await helper.close();
@@ -171,99 +44,14 @@ async function setupTopics() {
171
44
  setupTopics();
172
45
  ```
173
46
 
174
- ## Exactly-Once: Consume-Transform-Produce
175
-
176
- ```typescript
177
- import { KafkaProducerHelper, KafkaConsumerHelper } from '@venizia/ignis-helpers/kafka';
178
- import { stringSerializers, stringDeserializers } from '@platformatic/kafka';
179
-
180
- const producer = KafkaProducerHelper.newInstance({
181
- bootstrapBrokers: ['localhost:9092'],
182
- clientId: 'eos-producer',
183
- serializers: stringSerializers,
184
- transactionalId: 'eos-tx',
185
- idempotent: true,
186
- });
187
-
188
- const consumer = KafkaConsumerHelper.newInstance({
189
- bootstrapBrokers: ['localhost:9092'],
190
- clientId: 'eos-consumer',
191
- groupId: 'eos-group',
192
- deserializers: stringDeserializers,
193
- autocommit: false,
194
-
195
- onMessage: async ({ message }) => {
196
- // Consume-transform-produce within a single transaction
197
- const transformed = JSON.stringify({
198
- ...JSON.parse(message.value!),
199
- processedAt: new Date().toISOString(),
200
- });
201
-
202
- await producer.runInTransaction(async ({ send, addConsumer, addOffset }) => {
203
- await addConsumer(consumer.getConsumer());
204
- await addOffset(message);
205
- await send({
206
- messages: [{ topic: 'processed-events', key: message.key, value: transformed }],
207
- });
208
- });
209
- },
210
- onMessageError: ({ error }) => console.error('Processing error:', error),
211
- });
212
-
213
- await consumer.start({ topics: ['raw-events'] });
214
- ```
215
-
216
- ## Schema Registry: Validated Messages
217
-
218
- ```typescript
219
- import {
220
- KafkaSchemaRegistryHelper,
221
- KafkaProducerHelper,
222
- KafkaConsumerHelper,
223
- } from '@venizia/ignis-helpers/kafka';
224
-
225
- const registry = KafkaSchemaRegistryHelper.newInstance({
226
- url: 'http://localhost:8081',
227
- });
228
-
229
- const producer = KafkaProducerHelper.newInstance({
230
- bootstrapBrokers: ['localhost:9092'],
231
- clientId: 'schema-producer',
232
- registry: registry.getRegistry(),
233
- onBrokerConnect: ({ broker }) => console.log(`Producer connected to ${broker.host}`),
234
- });
235
-
236
- // Sends schema-validated objects
237
- await producer.getProducer().send({
238
- messages: [{
239
- topic: 'orders',
240
- key: 'order-1',
241
- value: { id: 1, status: 'created', total: 99.99 },
242
- }],
243
- });
244
-
245
- const consumer = KafkaConsumerHelper.newInstance({
246
- bootstrapBrokers: ['localhost:9092'],
247
- clientId: 'schema-consumer',
248
- groupId: 'schema-group',
249
- registry: registry.getRegistry(),
250
- onMessage: async ({ message }) => {
251
- // message.value is auto-deserialized to the schema type
252
- console.log(message.value.id, message.value.status);
253
- await message.commit();
254
- },
255
- });
256
-
257
- await consumer.start({ topics: ['orders'] });
258
- ```
259
-
260
47
  ## Using Helpers with IGNIS IoC
261
48
 
49
+ Bind each helper once at boot. Inject it wherever you need to publish or consume - the same pattern IGNIS uses for every other connected resource.
50
+
262
51
  ```typescript
263
52
  import {
264
53
  KafkaProducerHelper,
265
54
  KafkaConsumerHelper,
266
- KafkaAdminHelper,
267
55
  } from '@venizia/ignis-helpers/kafka';
268
56
  import { stringSerializers, stringDeserializers } from '@platformatic/kafka';
269
57
  import { inject } from '@venizia/ignis-inversion';
@@ -312,24 +100,22 @@ export class OrderEventService {
312
100
 
313
101
  ## Troubleshooting
314
102
 
315
- ### Common Issues
316
-
317
103
  | Error | Cause | Fix |
318
104
  |-------|-------|-----|
319
- | `ECONNREFUSED localhost:9092` | Broker `advertised.listeners` set to `localhost` but connecting remotely | Set `KAFKA_ADVERTISED_LISTENERS` with the correct external host IP |
320
- | `Request timed out` | SASL handshake or broker unreachable | Add `connectTimeout: 30_000, requestTimeout: 30_000` |
321
- | `Connection closed` | Connecting without SASL to a SASL-required listener | Check `KAFKA_LISTENER_SECURITY_PROTOCOL_MAP` -- use `SASL_PLAINTEXT` |
322
- | `Cannot find a suitable SASL mechanism` | Wrong mechanism (e.g., `PLAIN` when broker only supports `SCRAM-SHA-512`) | Check error message for supported mechanisms, match `mechanism` |
323
- | `Failed to deserialize a message` | Mismatch between serializer and deserializer | Ensure matching serde. For old data, use a new consumer group or recreate topic |
324
- | `JSON.stringify cannot serialize BigInt` | `message.offset` and `message.timestamp` are `bigint` | Use custom replacer: `(_k, v) => typeof v === 'bigint' ? v.toString() : v` |
325
- | Consumer idle (no messages) | More consumers than partitions | Ensure `numPartitions >= numConsumers` |
326
- | `isHealthy()` returns `false` | All brokers disconnected (a single idle disconnect won't trigger this) | Check broker addresses, SASL config, network connectivity. Use `getConnectedBrokerCount()` for details |
327
- | `isReady()` returns `false` (consumer) | Consumer not active -- `start()` not called or stream closed | Call `await helper.start({ topics })` before checking readiness |
328
- | Graceful shutdown timeout | In-flight requests taking too long | Increase `shutdownTimeout` or use `close({ isForce: true })` |
105
+ | `ECONNREFUSED localhost:9092` | Broker `advertised.listeners` set to `localhost` but you're connecting remotely | Set `KAFKA_ADVERTISED_LISTENERS` to the correct external host IP |
106
+ | `Request timed out` | SASL handshake stalled, or the broker is unreachable | Add `connectTimeout: 30_000, requestTimeout: 30_000` |
107
+ | `Connection closed` | Connecting without SASL to a SASL-required listener | Check `KAFKA_LISTENER_SECURITY_PROTOCOL_MAP` - use `SASL_PLAINTEXT` |
108
+ | `Cannot find a suitable SASL mechanism` | Wrong mechanism, e.g. `PLAIN` when the broker only supports `SCRAM-SHA-512` | Read the error for the supported mechanisms, match `mechanism` to one |
109
+ | `Failed to deserialize a message` | Serializer and deserializer don't match | Match the serde on both sides. For old data, use a new consumer group or recreate the topic |
110
+ | `JSON.stringify cannot serialize BigInt` | `message.offset` and `message.timestamp` are `bigint` | Use a custom replacer: `(_k, v) => typeof v === 'bigint' ? v.toString() : v` |
111
+ | Consumer sits idle, no messages | More consumers than partitions | Make sure `numPartitions >= numConsumers` |
112
+ | `isHealthy()` returns `false` | Every broker disconnected - one idle disconnect alone won't trigger this | Check broker addresses, SASL config, network. `getConnectedBrokerCount()` gives the exact count |
113
+ | `isReady()` returns `false` (consumer) | Consumer isn't active - `start()` was never called, or the stream closed | Call `await helper.start({ topics })` before checking readiness |
114
+ | Graceful shutdown times out | In-flight requests are taking too long | Raise `shutdownTimeout`, or call `close({ isForce: true })` |
329
115
 
330
116
  ### Docker Kafka Configuration
331
117
 
332
- When running Kafka in Docker and connecting from outside the container:
118
+ Connecting from outside a Dockerized Kafka needs two listeners: one for containers talking to each other, one for the host.
333
119
 
334
120
  ```yaml
335
121
  environment:
@@ -343,9 +129,11 @@ environment:
343
129
  CONTROLLER:PLAINTEXT
344
130
  ```
345
131
 
346
- - `INTERNAL` -- used for inter-broker communication
347
- - `EXTERNAL` -- used for client connections from outside Docker
348
- - `CONTROLLER` -- used for KRaft controller communication
132
+ | Listener | Used for |
133
+ |---|---|
134
+ | `INTERNAL` | Inter-broker communication |
135
+ | `EXTERNAL` | Client connections from outside Docker |
136
+ | `CONTROLLER` | KRaft controller communication |
349
137
 
350
138
  ## See also
351
139