@venizia/ignis-docs 0.0.7-2 → 0.0.8-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/dist/mcp-server/common/paths.d.ts +4 -2
- package/dist/mcp-server/common/paths.d.ts.map +1 -1
- package/dist/mcp-server/common/paths.js +8 -6
- package/dist/mcp-server/common/paths.js.map +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
- package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
- package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
- package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
- package/package.json +1 -1
- package/wiki/best-practices/api-usage-examples.md +9 -9
- package/wiki/best-practices/architectural-patterns.md +19 -3
- package/wiki/best-practices/architecture-decisions.md +6 -6
- package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
- package/wiki/best-practices/code-style-standards/index.md +2 -2
- package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
- package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
- package/wiki/best-practices/data-modeling.md +1 -1
- package/wiki/best-practices/deployment-strategies.md +1 -1
- package/wiki/best-practices/error-handling.md +2 -2
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/{references → extensions}/components/authentication/api.md +12 -20
- package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
- package/wiki/{references → extensions}/components/authentication/index.md +5 -8
- package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
- package/wiki/{references → extensions}/components/authorization/api.md +62 -13
- package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
- package/wiki/{references → extensions}/components/authorization/index.md +93 -6
- package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
- package/wiki/{references → extensions}/components/health-check.md +5 -4
- package/wiki/{references → extensions}/components/index.md +2 -0
- package/wiki/{references → extensions}/components/mail/index.md +1 -1
- package/wiki/{references → extensions}/components/request-tracker.md +1 -1
- package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
- package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
- package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
- package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
- package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
- package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
- package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
- package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
- package/wiki/{references → extensions}/components/swagger.md +3 -3
- package/wiki/{references → extensions}/components/template/index.md +4 -4
- package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
- package/wiki/{references → extensions}/components/template/single-page.md +1 -1
- package/wiki/{references → extensions}/components/websocket/api.md +7 -6
- package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
- package/wiki/{references → extensions}/components/websocket/index.md +17 -11
- package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
- package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
- package/wiki/{references → extensions}/helpers/env/index.md +9 -5
- package/wiki/{references → extensions}/helpers/error/index.md +2 -7
- package/wiki/{references → extensions}/helpers/index.md +18 -6
- package/wiki/{references → extensions}/helpers/kafka/admin.md +33 -16
- package/wiki/extensions/helpers/kafka/consumer.md +384 -0
- package/wiki/extensions/helpers/kafka/examples.md +361 -0
- package/wiki/extensions/helpers/kafka/index.md +639 -0
- package/wiki/{references → extensions}/helpers/kafka/producer.md +100 -96
- package/wiki/extensions/helpers/kafka/schema-registry.md +214 -0
- package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
- package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
- package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
- package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
- package/wiki/{references → extensions}/helpers/template/index.md +1 -1
- package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
- package/wiki/{references → extensions}/helpers/types/index.md +63 -16
- package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
- package/wiki/extensions/index.md +48 -0
- package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
- package/wiki/guides/core-concepts/application/index.md +95 -35
- package/wiki/guides/core-concepts/components-guide.md +23 -19
- package/wiki/guides/core-concepts/components.md +34 -10
- package/wiki/guides/core-concepts/dependency-injection.md +99 -34
- package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
- package/wiki/guides/core-concepts/persistent/models.md +43 -1
- package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
- package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
- package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
- package/wiki/guides/core-concepts/services.md +19 -5
- package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
- package/wiki/guides/get-started/philosophy.md +1 -1
- package/wiki/guides/index.md +2 -2
- package/wiki/guides/reference/glossary.md +7 -7
- package/wiki/guides/reference/mcp-docs-server.md +1 -1
- package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
- package/wiki/guides/tutorials/complete-installation.md +17 -14
- package/wiki/guides/tutorials/ecommerce-api.md +18 -18
- package/wiki/guides/tutorials/realtime-chat.md +8 -8
- package/wiki/guides/tutorials/testing.md +2 -2
- package/wiki/index.md +4 -3
- package/wiki/references/base/application.md +341 -21
- package/wiki/references/base/bootstrapping.md +43 -13
- package/wiki/references/base/components.md +259 -8
- package/wiki/references/base/controllers.md +556 -253
- package/wiki/references/base/datasources.md +159 -79
- package/wiki/references/base/dependency-injection.md +299 -48
- package/wiki/references/base/filter-system/application-usage.md +18 -2
- package/wiki/references/base/filter-system/array-operators.md +14 -6
- package/wiki/references/base/filter-system/comparison-operators.md +9 -3
- package/wiki/references/base/filter-system/default-filter.md +28 -3
- package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
- package/wiki/references/base/filter-system/index.md +169 -11
- package/wiki/references/base/filter-system/json-filtering.md +51 -18
- package/wiki/references/base/filter-system/list-operators.md +4 -3
- package/wiki/references/base/filter-system/logical-operators.md +7 -2
- package/wiki/references/base/filter-system/null-operators.md +50 -0
- package/wiki/references/base/filter-system/quick-reference.md +82 -243
- package/wiki/references/base/filter-system/range-operators.md +7 -1
- package/wiki/references/base/filter-system/tips.md +34 -7
- package/wiki/references/base/filter-system/use-cases.md +6 -5
- package/wiki/references/base/grpc-controllers.md +984 -0
- package/wiki/references/base/index.md +32 -24
- package/wiki/references/base/middleware.md +347 -0
- package/wiki/references/base/models.md +390 -46
- package/wiki/references/base/providers.md +14 -14
- package/wiki/references/base/repositories/advanced.md +84 -69
- package/wiki/references/base/repositories/index.md +447 -12
- package/wiki/references/base/repositories/mixins.md +103 -98
- package/wiki/references/base/repositories/relations.md +129 -45
- package/wiki/references/base/repositories/soft-deletable.md +104 -23
- package/wiki/references/base/services.md +94 -14
- package/wiki/references/index.md +12 -10
- package/wiki/references/quick-reference.md +98 -65
- package/wiki/references/utilities/crypto.md +21 -4
- package/wiki/references/utilities/date.md +25 -7
- package/wiki/references/utilities/index.md +26 -24
- package/wiki/references/utilities/jsx.md +54 -54
- package/wiki/references/utilities/module.md +8 -6
- package/wiki/references/utilities/parse.md +16 -9
- package/wiki/references/utilities/performance.md +22 -7
- package/wiki/references/utilities/promise.md +19 -16
- package/wiki/references/utilities/request.md +48 -26
- package/wiki/references/utilities/schema.md +69 -6
- package/wiki/references/utilities/statuses.md +131 -140
- package/wiki/references/helpers/kafka/consumer.md +0 -473
- package/wiki/references/helpers/kafka/examples.md +0 -234
- package/wiki/references/helpers/kafka/index.md +0 -482
- /package/wiki/{references → extensions}/components/mail/api.md +0 -0
- /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
- /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
- /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
- /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
- /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
- /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
- /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
- /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# Examples & Troubleshooting
|
|
2
|
+
|
|
3
|
+
Complete examples and common issue resolution for the Kafka helpers.
|
|
4
|
+
|
|
5
|
+
## Producer: Send Messages with Health Monitoring
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { KafkaProducerHelper, KafkaAcks } from '@venizia/ignis-helpers/kafka';
|
|
9
|
+
import { stringSerializers } from '@platformatic/kafka';
|
|
10
|
+
|
|
11
|
+
const helper = KafkaProducerHelper.newInstance({
|
|
12
|
+
bootstrapBrokers: ['broker1:9092', 'broker2:9092'],
|
|
13
|
+
clientId: 'interval-producer',
|
|
14
|
+
serializers: stringSerializers,
|
|
15
|
+
acks: KafkaAcks.ALL,
|
|
16
|
+
onBrokerConnect: ({ broker }) => console.log(`Connected to ${broker.host}:${broker.port}`),
|
|
17
|
+
onBrokerDisconnect: ({ broker }) => console.warn(`Disconnected from ${broker.host}`),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const producer = helper.getProducer();
|
|
21
|
+
let count = 0;
|
|
22
|
+
|
|
23
|
+
const interval = setInterval(async () => {
|
|
24
|
+
if (!helper.isHealthy()) {
|
|
25
|
+
console.warn('Producer not healthy, skipping...');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
await producer.send({
|
|
30
|
+
messages: [{
|
|
31
|
+
topic: 'events',
|
|
32
|
+
key: `key-${count % 3}`,
|
|
33
|
+
value: JSON.stringify({ index: count, timestamp: new Date().toISOString() }),
|
|
34
|
+
}],
|
|
35
|
+
});
|
|
36
|
+
count++;
|
|
37
|
+
}, 100);
|
|
38
|
+
|
|
39
|
+
process.on('SIGINT', async () => {
|
|
40
|
+
clearInterval(interval);
|
|
41
|
+
console.log(`Shutting down... (sent ${count} messages)`);
|
|
42
|
+
await helper.close();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Consumer: Callback-Based with Lag Monitoring
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { KafkaConsumerHelper } from '@venizia/ignis-helpers/kafka';
|
|
51
|
+
import { stringDeserializers } from '@platformatic/kafka';
|
|
52
|
+
|
|
53
|
+
const helper = KafkaConsumerHelper.newInstance({
|
|
54
|
+
bootstrapBrokers: ['broker1:9092', 'broker2:9092'],
|
|
55
|
+
clientId: 'event-consumer',
|
|
56
|
+
groupId: 'processing-group',
|
|
57
|
+
deserializers: stringDeserializers,
|
|
58
|
+
|
|
59
|
+
onMessage: async ({ message }) => {
|
|
60
|
+
const data = JSON.parse(message.value!);
|
|
61
|
+
console.log(`Processing: ${message.key} -> ${JSON.stringify(data)}`);
|
|
62
|
+
await message.commit();
|
|
63
|
+
},
|
|
64
|
+
onMessageDone: ({ message }) => {
|
|
65
|
+
console.log(`Done: ${message.key}`);
|
|
66
|
+
},
|
|
67
|
+
onMessageError: ({ error, message }) => {
|
|
68
|
+
console.error(`Error processing ${message?.key}:`, error.message);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
onGroupJoin: ({ groupId, memberId }) => {
|
|
72
|
+
console.log(`Joined group ${groupId} as ${memberId}`);
|
|
73
|
+
},
|
|
74
|
+
onGroupRebalance: ({ groupId }) => {
|
|
75
|
+
console.log(`Rebalance in ${groupId}`);
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
onLag: ({ lag }) => {
|
|
79
|
+
for (const [topic, partitionLags] of lag) {
|
|
80
|
+
partitionLags.forEach((lagValue, partition) => {
|
|
81
|
+
if (lagValue > 1000n) {
|
|
82
|
+
console.warn(`High lag on ${topic}[${partition}]: ${lagValue}`);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
onLagError: ({ error }) => console.error('Lag error:', error),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await helper.start({ topics: ['events'] });
|
|
91
|
+
helper.startLagMonitoring({ topics: ['events'], interval: 10_000 });
|
|
92
|
+
|
|
93
|
+
process.on('SIGINT', async () => {
|
|
94
|
+
await helper.close();
|
|
95
|
+
process.exit(0);
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Consumer: Direct Stream Access (for-await)
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { KafkaConsumerHelper } from '@venizia/ignis-helpers/kafka';
|
|
103
|
+
import { stringDeserializers } from '@platformatic/kafka';
|
|
104
|
+
|
|
105
|
+
const helper = KafkaConsumerHelper.newInstance({
|
|
106
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
107
|
+
clientId: 'stream-consumer',
|
|
108
|
+
groupId: 'stream-group',
|
|
109
|
+
deserializers: stringDeserializers,
|
|
110
|
+
onBrokerConnect: ({ broker }) => console.log(`Connected to ${broker.host}`),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Use the consumer directly for async iterator pattern
|
|
114
|
+
const consumer = helper.getConsumer();
|
|
115
|
+
const stream = await consumer.consume({
|
|
116
|
+
topics: ['orders'],
|
|
117
|
+
mode: 'committed',
|
|
118
|
+
fallbackMode: 'latest',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
for await (const message of stream) {
|
|
122
|
+
console.log(`${message.topic}[${message.partition}] @${message.offset}: ${message.key} -> ${message.value}`);
|
|
123
|
+
await message.commit();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await stream.close();
|
|
127
|
+
await helper.close();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Admin: Topic Setup Script
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { KafkaAdminHelper } from '@venizia/ignis-helpers/kafka';
|
|
134
|
+
|
|
135
|
+
async function setupTopics() {
|
|
136
|
+
const helper = KafkaAdminHelper.newInstance({
|
|
137
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
138
|
+
clientId: 'topic-setup',
|
|
139
|
+
onBrokerConnect: ({ broker }) => console.log(`Connected to ${broker.host}`),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const admin = helper.getAdmin();
|
|
143
|
+
|
|
144
|
+
// Create topics
|
|
145
|
+
await admin.createTopics({
|
|
146
|
+
topics: ['orders', 'inventory', 'notifications'],
|
|
147
|
+
partitions: 6,
|
|
148
|
+
replicas: 3,
|
|
149
|
+
configs: [
|
|
150
|
+
{ name: 'retention.ms', value: '604800000' },
|
|
151
|
+
{ name: 'compression.type', value: 'zstd' },
|
|
152
|
+
],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Verify
|
|
156
|
+
const topics = await admin.listTopics({ includeInternals: false });
|
|
157
|
+
console.log('Topics:', topics);
|
|
158
|
+
|
|
159
|
+
// Health check
|
|
160
|
+
console.log('Healthy:', helper.isHealthy());
|
|
161
|
+
|
|
162
|
+
await helper.close();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
setupTopics();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Exactly-Once: Consume-Transform-Produce
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { KafkaProducerHelper, KafkaConsumerHelper } from '@venizia/ignis-helpers/kafka';
|
|
172
|
+
import { stringSerializers, stringDeserializers } from '@platformatic/kafka';
|
|
173
|
+
|
|
174
|
+
const producer = KafkaProducerHelper.newInstance({
|
|
175
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
176
|
+
clientId: 'eos-producer',
|
|
177
|
+
serializers: stringSerializers,
|
|
178
|
+
transactionalId: 'eos-tx',
|
|
179
|
+
idempotent: true,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const consumer = KafkaConsumerHelper.newInstance({
|
|
183
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
184
|
+
clientId: 'eos-consumer',
|
|
185
|
+
groupId: 'eos-group',
|
|
186
|
+
deserializers: stringDeserializers,
|
|
187
|
+
autocommit: false,
|
|
188
|
+
|
|
189
|
+
onMessage: async ({ message }) => {
|
|
190
|
+
// Consume-transform-produce within a single transaction
|
|
191
|
+
const transformed = JSON.stringify({
|
|
192
|
+
...JSON.parse(message.value!),
|
|
193
|
+
processedAt: new Date().toISOString(),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
await producer.runInTransaction(async ({ send, addConsumer, addOffset }) => {
|
|
197
|
+
await addConsumer(consumer.getConsumer());
|
|
198
|
+
await addOffset(message);
|
|
199
|
+
await send({
|
|
200
|
+
messages: [{ topic: 'processed-events', key: message.key, value: transformed }],
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
onMessageError: ({ error }) => console.error('Processing error:', error),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await consumer.start({ topics: ['raw-events'] });
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Schema Registry: Validated Messages
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import {
|
|
214
|
+
KafkaSchemaRegistryHelper,
|
|
215
|
+
KafkaProducerHelper,
|
|
216
|
+
KafkaConsumerHelper,
|
|
217
|
+
} from '@venizia/ignis-helpers/kafka';
|
|
218
|
+
|
|
219
|
+
const registry = KafkaSchemaRegistryHelper.newInstance({
|
|
220
|
+
url: 'http://localhost:8081',
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const producer = KafkaProducerHelper.newInstance({
|
|
224
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
225
|
+
clientId: 'schema-producer',
|
|
226
|
+
registry: registry.getRegistry(),
|
|
227
|
+
onBrokerConnect: ({ broker }) => console.log(`Producer connected to ${broker.host}`),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Sends schema-validated objects
|
|
231
|
+
await producer.getProducer().send({
|
|
232
|
+
messages: [{
|
|
233
|
+
topic: 'orders',
|
|
234
|
+
key: 'order-1',
|
|
235
|
+
value: { id: 1, status: 'created', total: 99.99 },
|
|
236
|
+
}],
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const consumer = KafkaConsumerHelper.newInstance({
|
|
240
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
241
|
+
clientId: 'schema-consumer',
|
|
242
|
+
groupId: 'schema-group',
|
|
243
|
+
registry: registry.getRegistry(),
|
|
244
|
+
onMessage: async ({ message }) => {
|
|
245
|
+
// message.value is auto-deserialized to the schema type
|
|
246
|
+
console.log(message.value.id, message.value.status);
|
|
247
|
+
await message.commit();
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
await consumer.start({ topics: ['orders'] });
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Using Helpers with Ignis IoC
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import {
|
|
258
|
+
KafkaProducerHelper,
|
|
259
|
+
KafkaConsumerHelper,
|
|
260
|
+
KafkaAdminHelper,
|
|
261
|
+
} from '@venizia/ignis-helpers/kafka';
|
|
262
|
+
import { stringSerializers, stringDeserializers } from '@platformatic/kafka';
|
|
263
|
+
import { inject, injectable } from '@venizia/ignis-inversion';
|
|
264
|
+
|
|
265
|
+
// Register helpers in the IoC container
|
|
266
|
+
app.bind('kafka.producer').to(
|
|
267
|
+
KafkaProducerHelper.newInstance({
|
|
268
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
269
|
+
clientId: 'order-service-producer',
|
|
270
|
+
serializers: stringSerializers,
|
|
271
|
+
onBrokerConnect: ({ broker }) => console.log(`Producer -> ${broker.host}`),
|
|
272
|
+
}),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
app.bind('kafka.consumer').to(
|
|
276
|
+
KafkaConsumerHelper.newInstance({
|
|
277
|
+
bootstrapBrokers: ['localhost:9092'],
|
|
278
|
+
clientId: 'order-service-consumer',
|
|
279
|
+
groupId: 'order-service',
|
|
280
|
+
deserializers: stringDeserializers,
|
|
281
|
+
onMessage: async ({ message }) => {
|
|
282
|
+
// Handled by the service below
|
|
283
|
+
},
|
|
284
|
+
onBrokerConnect: ({ broker }) => console.log(`Consumer -> ${broker.host}`),
|
|
285
|
+
}),
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Inject into services
|
|
289
|
+
@injectable()
|
|
290
|
+
export class OrderEventService {
|
|
291
|
+
constructor(
|
|
292
|
+
@inject({ key: 'kafka.producer' }) private producer: KafkaProducerHelper,
|
|
293
|
+
@inject({ key: 'kafka.consumer' }) private consumer: KafkaConsumerHelper,
|
|
294
|
+
) {}
|
|
295
|
+
|
|
296
|
+
async publishOrderCreated(orderId: string, data: Record<string, unknown>) {
|
|
297
|
+
await this.producer.getProducer().send({
|
|
298
|
+
messages: [{ topic: 'order-events', key: orderId, value: JSON.stringify(data) }],
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async startConsuming() {
|
|
303
|
+
await this.consumer.start({ topics: ['order-events'] });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Troubleshooting
|
|
309
|
+
|
|
310
|
+
### Common Issues
|
|
311
|
+
|
|
312
|
+
| Error | Cause | Fix |
|
|
313
|
+
|-------|-------|-----|
|
|
314
|
+
| `ECONNREFUSED localhost:9092` | Broker `advertised.listeners` set to `localhost` but connecting remotely | Set `KAFKA_ADVERTISED_LISTENERS` with the correct external host IP |
|
|
315
|
+
| `Request timed out` | SASL handshake or broker unreachable | Add `connectTimeout: 30_000, requestTimeout: 30_000` |
|
|
316
|
+
| `Connection closed` | Connecting without SASL to a SASL-required listener | Check `KAFKA_LISTENER_SECURITY_PROTOCOL_MAP` -- use `SASL_PLAINTEXT` |
|
|
317
|
+
| `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` |
|
|
318
|
+
| `Failed to deserialize a message` | Mismatch between serializer and deserializer | Ensure matching serde. For old data, use a new consumer group or recreate topic |
|
|
319
|
+
| `JSON.stringify cannot serialize BigInt` | `message.offset` and `message.timestamp` are `bigint` | Use custom replacer: `(_k, v) => typeof v === 'bigint' ? v.toString() : v` |
|
|
320
|
+
| Consumer idle (no messages) | More consumers than partitions | Ensure `numPartitions >= numConsumers` |
|
|
321
|
+
| `isHealthy()` returns `false` | No broker connected yet, or connection lost | Check broker addresses, SASL config, network connectivity |
|
|
322
|
+
| `isReady()` returns `false` (consumer) | Consumer not active -- `start()` not called or stream closed | Call `await helper.start({ topics })` before checking readiness |
|
|
323
|
+
| Graceful shutdown timeout | In-flight requests taking too long | Increase `shutdownTimeout` or use `close({ isForce: true })` |
|
|
324
|
+
|
|
325
|
+
### Docker Kafka Configuration
|
|
326
|
+
|
|
327
|
+
When running Kafka in Docker and connecting from outside the container:
|
|
328
|
+
|
|
329
|
+
```yaml
|
|
330
|
+
environment:
|
|
331
|
+
DOCKER_HOST_IP: '192.168.1.100' # Your host machine's IP
|
|
332
|
+
KAFKA_ADVERTISED_LISTENERS: >
|
|
333
|
+
INTERNAL://kafka-1:29092,
|
|
334
|
+
EXTERNAL://${DOCKER_HOST_IP}:19092
|
|
335
|
+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: >
|
|
336
|
+
INTERNAL:PLAINTEXT,
|
|
337
|
+
EXTERNAL:SASL_PLAINTEXT,
|
|
338
|
+
CONTROLLER:PLAINTEXT
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
- `INTERNAL` -- used for inter-broker communication
|
|
342
|
+
- `EXTERNAL` -- used for client connections from outside Docker
|
|
343
|
+
- `CONTROLLER` -- used for KRaft controller communication
|
|
344
|
+
|
|
345
|
+
## See Also
|
|
346
|
+
|
|
347
|
+
- **Kafka Pages:**
|
|
348
|
+
- [Overview & Fundamentals](./) -- Connection, serialization, constants, compression
|
|
349
|
+
- [Producer](./producer) -- Producer helper, transactions, API reference
|
|
350
|
+
- [Consumer](./consumer) -- Consumer helper, callbacks, lag monitoring, API reference
|
|
351
|
+
- [Admin](./admin) -- Admin helper & API reference
|
|
352
|
+
- [Schema Registry](./schema-registry) -- Schema registry helper
|
|
353
|
+
|
|
354
|
+
- **Other Helpers:**
|
|
355
|
+
- [Queue Helper](../queue/) -- BullMQ, MQTT, and in-memory queues
|
|
356
|
+
- [Redis Helper](../redis/) -- Redis connection management
|
|
357
|
+
|
|
358
|
+
- **External Resources:**
|
|
359
|
+
- [@platformatic/kafka](https://github.com/platformatic/kafka) -- Underlying Kafka client library
|
|
360
|
+
- [Apache Kafka Documentation](https://kafka.apache.org/documentation/) -- Official Kafka docs
|
|
361
|
+
- [KIP-848](https://cwiki.apache.org/confluence/display/KAFKA/KIP-848) -- New consumer group protocol
|