├── .prettierrc
├── nest-cli.json
├── src
├── app
│ ├── constant.ts
│ ├── consumer
│ │ ├── consumer.module.ts
│ │ └── consumer.service.ts
│ ├── default
│ │ ├── default.module.ts
│ │ ├── default.controller.ts
│ │ └── default.service.ts
│ └── common
│ │ └── kafka
│ │ ├── kafka.message.ts
│ │ ├── kafka.module.ts
│ │ ├── kafka.decorator.ts
│ │ └── kafka.service.ts
├── main.ts
└── app.module.ts
├── tsconfig.build.json
├── test
├── jest-e2e.json
└── app.e2e-spec.ts
├── docker
├── kafka-drop-ui.yml
└── kafka.yml
├── ts-nestjs-kafka-.iml
├── tsconfig.json
├── tslint.json
├── .gitignore
├── README.md
└── package.json
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "collection": "@nestjs/schematics",
3 | "sourceRoot": "src"
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/constant.ts:
--------------------------------------------------------------------------------
1 | export const HELLO_TOPIC = 'hello.topic';
2 | export const HELLO_FIXED_TOPIC = 'hello.fixed.topic';
3 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/consumer/consumer.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConsumerService } from './consumer.service';
3 |
4 | @Module({
5 | providers: [ConsumerService],
6 | })
7 | export class ConsumerModule {}
8 |
--------------------------------------------------------------------------------
/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 |
4 | async function bootstrap() {
5 | const app = await NestFactory.create(AppModule);
6 | await app.listen(3000);
7 | }
8 | bootstrap();
9 |
--------------------------------------------------------------------------------
/docker/kafka-drop-ui.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 |
3 | services:
4 | kafkadrop:
5 | hostname: kafkadrop
6 | image: obsidiandynamics/kafdrop:latest
7 | environment:
8 | - KAFKA_BROKERCONNECT=kafka:19092
9 | ports:
10 | - 9000:9000
11 |
12 | networks:
13 | elastic:
14 | driver: bridge
15 |
--------------------------------------------------------------------------------
/src/app/default/default.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DefaultService } from './default.service';
3 | import { DefaultController } from './default.controller';
4 |
5 | @Module({
6 | providers: [DefaultService],
7 | controllers: [DefaultController],
8 | })
9 | export class DefaultModule {}
10 |
--------------------------------------------------------------------------------
/ts-nestjs-kafka-.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "target": "es2017",
9 | "sourceMap": true,
10 | "outDir": "./dist",
11 | "baseUrl": "./",
12 | "incremental": true
13 | },
14 | "exclude": ["node_modules", "dist"]
15 | }
16 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": ["tslint:recommended"],
4 | "jsRules": {
5 | "no-unused-expression": true
6 | },
7 | "rules": {
8 | "quotemark": [true, "single"],
9 | "member-access": [false],
10 | "ordered-imports": [false],
11 | "max-line-length": [true, 150],
12 | "member-ordering": [false],
13 | "interface-name": [false],
14 | "arrow-parens": false,
15 | "object-literal-sort-keys": false
16 | },
17 | "rulesDirectory": []
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # OS
14 | .DS_Store
15 |
16 | # Tests
17 | /coverage
18 | /.nyc_output
19 |
20 | # IDEs and editors
21 | /.idea
22 | .project
23 | .classpath
24 | .c9/
25 | *.launch
26 | .settings/
27 | *.sublime-workspace
28 |
29 | # IDE - VSCode
30 | .vscode/*
31 | !.vscode/settings.json
32 | !.vscode/tasks.json
33 | !.vscode/launch.json
34 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DefaultModule } from './app/default/default.module';
3 | import { ConsumerModule } from './app/consumer/consumer.module';
4 | import { KafkaModule } from './app/common/kafka/kafka.module';
5 |
6 | @Module({
7 | imports: [
8 | KafkaModule.register({
9 | clientId: 'test-app-client',
10 | brokers: ['localhost:9092'],
11 | groupId: 'test-app-group',
12 | }),
13 | DefaultModule,
14 | ConsumerModule,
15 | ],
16 | })
17 | export class AppModule {}
18 |
--------------------------------------------------------------------------------
/src/app/common/kafka/kafka.message.ts:
--------------------------------------------------------------------------------
1 | export class KafkaPayload {
2 | public body: any;
3 | public messageId: string;
4 | public messageType: string;
5 | public topicName: string;
6 | public createdTime?: string;
7 |
8 | create?(messageId, body, messageType, topicName): KafkaPayload {
9 | return {
10 | messageId,
11 | body,
12 | messageType,
13 | topicName,
14 | createdTime: new Date().toISOString(),
15 | };
16 | }
17 | }
18 |
19 | export declare class KafkaConfig {
20 | clientId: string;
21 | brokers: string[];
22 | groupId: string;
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/common/kafka/kafka.module.ts:
--------------------------------------------------------------------------------
1 | import { DynamicModule, Global, Module } from '@nestjs/common';
2 | import { KafkaService } from './kafka.service';
3 | import { KafkaConfig } from './kafka.message';
4 |
5 | @Global()
6 | @Module({})
7 | export class KafkaModule {
8 | static register(kafkaConfig: KafkaConfig): DynamicModule {
9 | return {
10 | global: true,
11 | module: KafkaModule,
12 | providers: [
13 | {
14 | provide: KafkaService,
15 | useValue: new KafkaService(kafkaConfig),
16 | },
17 | ],
18 | exports: [KafkaService],
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { INestApplication } from '@nestjs/common';
3 | import * as request from 'supertest';
4 | import { AppModule } from './../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeEach(async () => {
10 | const moduleFixture: TestingModule = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | it('/ (GET)', () => {
19 | return request(app.getHttpServer())
20 | .get('/')
21 | .expect(200)
22 | .expect('Hello World!');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/app/default/default.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { DefaultService } from './default.service';
3 |
4 | @Controller()
5 | export class DefaultController {
6 | constructor(private readonly appService: DefaultService) {}
7 |
8 | @Get()
9 | getHello() {
10 | return this.appService.getHello();
11 | }
12 |
13 | @Get('/send')
14 | async send() {
15 | await this.appService.sendToFixedConsumer();
16 | return this.appService.send();
17 | }
18 |
19 | @Get('/send/consumer')
20 | async sendToConsumer() {
21 | return await this.appService.send();
22 | }
23 |
24 | @Get('/send/fixed-consumer')
25 | async sendToFixedConsumer() {
26 | return await this.appService.sendToFixedConsumer();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/common/kafka/kafka.decorator.ts:
--------------------------------------------------------------------------------
1 | export const SUBSCRIBER_FN_REF_MAP = new Map();
2 | export const SUBSCRIBER_FIXED_FN_REF_MAP = new Map();
3 | export const SUBSCRIBER_OBJ_REF_MAP = new Map();
4 |
5 | export function SubscribeTo(topic) {
6 | return (target, propertyKey, descriptor) => {
7 | const originalMethod = target[propertyKey];
8 | SUBSCRIBER_FN_REF_MAP.set(topic, originalMethod);
9 | SUBSCRIBER_OBJ_REF_MAP.set(topic, target);
10 | return descriptor;
11 | };
12 | }
13 | export function SubscribeToFixedGroup(topic) {
14 | return (target, propertyKey, descriptor) => {
15 | const originalMethod = target[propertyKey];
16 | SUBSCRIBER_FIXED_FN_REF_MAP.set(topic, originalMethod);
17 | SUBSCRIBER_OBJ_REF_MAP.set(topic, target);
18 | return descriptor;
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/docker/kafka.yml:
--------------------------------------------------------------------------------
1 | # https://kafka.js.org/docs/running-kafka-in-development
2 | version: '2'
3 | services:
4 | zookeeper:
5 | image: wurstmeister/zookeeper:latest
6 | ports:
7 | - 2181:2181
8 | volumes:
9 | - ./_data/zookeeper/data:/data
10 | kafka:
11 | image: wurstmeister/kafka:2.12-2.5.0
12 | hostname: kafka
13 | #container_name: kafka
14 | ports:
15 | - 9092:9092
16 | environment:
17 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
18 | KAFKA_LISTENERS: LISTENER_DOCKER_INTERNAL://:19092,LISTENER_DOCKER_EXTERNAL://:9092
19 | KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka:19092,LISTENER_DOCKER_EXTERNAL://127.0.0.1:9092
20 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LISTENER_DOCKER_INTERNAL:PLAINTEXT,LISTENER_DOCKER_EXTERNAL:PLAINTEXT
21 | KAFKA_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER_INTERNAL
22 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
23 | KAFKA_BROKER_ID: 1
24 | KAFKA_CREATE_TOPICS: __consumer_offsets:50:1,user.create:1:1
25 | volumes:
26 | - ./_data/kafka/data:/var/lib/kafka/data
27 | depends_on:
28 | - zookeeper
29 |
30 | networks:
31 | elastic:
32 | driver: bridge
33 |
--------------------------------------------------------------------------------
/src/app/consumer/consumer.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import {
3 | SubscribeTo,
4 | SubscribeToFixedGroup,
5 | } from '../common/kafka/kafka.decorator';
6 | import { KafkaPayload } from '../common/kafka/kafka.message';
7 | import { HELLO_FIXED_TOPIC } from '../constant';
8 |
9 | @Injectable()
10 | export class ConsumerService {
11 | /**
12 | * When group id is unique for every container.
13 | * @param payload
14 | */
15 | @SubscribeTo('hello.topic')
16 | helloSubscriber(payload: KafkaPayload) {
17 | console.log('[KAKFA-CONSUMER] Print message after receiving', payload);
18 | }
19 |
20 | /**
21 | * When application or container scale up &
22 | * consumer group id is same for application
23 | * @param payload
24 | */
25 | @SubscribeToFixedGroup(HELLO_FIXED_TOPIC)
26 | helloSubscriberToFixedGroup(payload: KafkaPayload) {
27 | console.log(
28 | '[KAKFA-CONSUMER] Print message after receiving for fixed group',
29 | payload,
30 | );
31 | }
32 |
33 | /**
34 | * When group id is unique for every container.
35 | * @param payload
36 | */
37 | @SubscribeTo('hello.topic2')
38 | helloSubscriber2(payload: KafkaPayload) {
39 | console.log('[KAKFA-CONSUMER] Print message after receiving', payload);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/default/default.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { KafkaService } from '../common/kafka/kafka.service';
3 | import { KafkaPayload } from '../common/kafka/kafka.message';
4 | import { HELLO_FIXED_TOPIC } from '../constant';
5 |
6 | @Injectable()
7 | export class DefaultService {
8 | constructor(private readonly kafkaService: KafkaService) {}
9 |
10 | getHello() {
11 | return {
12 | value: 'hello world',
13 | };
14 | }
15 |
16 | async send() {
17 | const message = {
18 | value: 'Message send to Kakfa Topic',
19 | };
20 | const payload: KafkaPayload = {
21 | messageId: '' + new Date().valueOf(),
22 | body: message,
23 | messageType: 'Say.Hello',
24 | topicName: 'hello.topic',
25 | };
26 | const value = await this.kafkaService.sendMessage('hello.topic', payload);
27 | console.log('kafka status ', value);
28 | return message;
29 | }
30 |
31 | async sendToFixedConsumer() {
32 | const message = {
33 | value: 'Message send to Kakfa Topic',
34 | };
35 | const payload: KafkaPayload = {
36 | messageId: '' + new Date().valueOf(),
37 | body: message,
38 | messageType: 'Say.Hello',
39 | topicName: HELLO_FIXED_TOPIC, // topic name could be any name
40 | };
41 | const value = await this.kafkaService.sendMessage(
42 | HELLO_FIXED_TOPIC,
43 | payload,
44 | );
45 | console.log('kafka status ', value);
46 | return message;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
6 | [travis-url]: https://travis-ci.org/nestjs/nest
7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
8 | [linux-url]: https://travis-ci.org/nestjs/nest
9 |
10 | # Example of NestJs & KafkaJs integration simplified.
11 |
12 | ## Description
13 | This example built on
14 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
15 |
16 | It integrates with kafkajs and simplified to subscribe and publish message to kafka topics.
17 |
18 | Find more in -
19 | https://dev.to/rajeshkumarbehura/kafkajs-nestjs-with-typescript-simplified-example-35ep
20 |
21 | ## Installation
22 |
23 | ```bash
24 | $ yarn install
25 | ```
26 |
27 | ## Running the app
28 |
29 | #### Step- 1
30 | Make sure docker & docker-compose is installed.
31 |
32 | ```
33 | # Run kafka server in local machine, go to docker folder
34 | $ docker-compose -f kafka.yml up
35 | ```
36 |
37 | ```
38 | # To run kafka drop UI to monitor kafka in local machine, go to docker folder
39 | $ docker-compose -f kafka-drop-ui.yml up
40 |
41 | ```
42 | After running Kafka Server & Kafka Drop UI successuflly, access kafka-drop monitoring UI
43 | http://localhost:9000
44 |
45 | More on kafka drop Application -
46 | https://github.com/obsidiandynamics/kafdrop
47 | https://raw.githubusercontent.com/obsidiandynamics/kafdrop/master/docs/images/overview.png
48 |
49 | #### Step- 2
50 | Run the application
51 | ```bash
52 | # development
53 | $ yarn start
54 |
55 | ```
56 |
57 | #### Step- 3
58 | Send message to Kafka & receive message by consumer testing.
59 | 1. http://localhost:3000/send/consumer - send a sample message to kafka & consumer receives and print in application log
60 | 2. http://localhost:3000/send/fixed-consumer
61 |
62 | When you call above api, application console log will print " [KAKFA-CONSUMER] ...." .
63 |
64 |
65 |
66 | ## License
67 | Nest is [MIT licensed](LICENSE).
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ts-nestjs-kafka",
3 | "version": "0.0.2",
4 | "description": "
",
5 | "author": "",
6 | "license": "MIT",
7 | "scripts": {
8 | "prebuild": "rimraf dist",
9 | "build": "nest build",
10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
11 | "start": "nest start",
12 | "start:dev": "nest start --watch",
13 | "start:debug": "nest start --debug --watch",
14 | "start:prod": "node dist/main",
15 | "lint": "tslint -p tsconfig.json -c tslint.json",
16 | "test": "jest",
17 | "test:watch": "jest --watch",
18 | "test:cov": "jest --coverage",
19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
20 | "test:e2e": "jest --config ./test/jest-e2e.json"
21 | },
22 | "dependencies": {
23 | "@nestjs/common": "^6.7.2",
24 | "@nestjs/core": "^6.7.2",
25 | "@nestjs/platform-express": "^6.7.2",
26 | "kafkajs": "^1.12.0",
27 | "reflect-metadata": "^0.1.13",
28 | "rimraf": "^3.0.0",
29 | "rxjs": "^6.5.3"
30 | },
31 | "devDependencies": {
32 | "@nestjs/cli": "^6.9.0",
33 | "@nestjs/schematics": "^6.7.0",
34 | "@nestjs/testing": "^6.7.1",
35 | "@types/express": "^4.17.1",
36 | "@types/jest": "^24.0.18",
37 | "@types/node": "^12.7.5",
38 | "@types/supertest": "^2.0.8",
39 | "jest": "^24.9.0",
40 | "prettier": "^1.18.2",
41 | "supertest": "^4.0.2",
42 | "ts-jest": "^24.1.0",
43 | "ts-loader": "^6.1.1",
44 | "ts-node": "^8.4.1",
45 | "tsconfig-paths": "^3.9.0",
46 | "tslint": "^5.20.0",
47 | "typescript": "^3.6.3"
48 | },
49 | "jest": {
50 | "moduleFileExtensions": [
51 | "js",
52 | "json",
53 | "ts"
54 | ],
55 | "rootDir": "src",
56 | "testRegex": ".spec.ts$",
57 | "transform": {
58 | "^.+\\.(t|j)s$": "ts-jest"
59 | },
60 | "coverageDirectory": "./coverage",
61 | "testEnvironment": "node"
62 | },
63 | "main": "index.js",
64 | "directories": {
65 | "test": "test"
66 | },
67 | "keywords": []
68 | }
69 |
--------------------------------------------------------------------------------
/src/app/common/kafka/kafka.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
2 | import { Consumer, Kafka, Producer } from 'kafkajs';
3 | import {
4 | SUBSCRIBER_FIXED_FN_REF_MAP,
5 | SUBSCRIBER_FN_REF_MAP,
6 | SUBSCRIBER_OBJ_REF_MAP,
7 | } from './kafka.decorator';
8 | import { KafkaConfig, KafkaPayload } from './kafka.message';
9 |
10 | @Injectable()
11 | export class KafkaService implements OnModuleInit, OnModuleDestroy {
12 | private kafka: Kafka;
13 | private producer: Producer;
14 | private consumer: Consumer;
15 | private fixedConsumer: Consumer;
16 | private readonly consumerSuffix = '-' + Math.floor(Math.random() * 100000);
17 |
18 | constructor(private kafkaConfig: KafkaConfig) {
19 | this.kafka = new Kafka({
20 | clientId: this.kafkaConfig.clientId,
21 | brokers: this.kafkaConfig.brokers,
22 | });
23 | this.producer = this.kafka.producer();
24 | this.consumer = this.kafka.consumer({
25 | groupId: this.kafkaConfig.groupId + this.consumerSuffix,
26 | });
27 | this.fixedConsumer = this.kafka.consumer({
28 | groupId: this.kafkaConfig.groupId,
29 | });
30 | }
31 |
32 | async onModuleInit(): Promise {
33 | await this.connect();
34 | SUBSCRIBER_FN_REF_MAP.forEach((functionRef, topic) => {
35 | // attach the function with kafka topic name
36 | this.bindAllTopicToConsumer(functionRef, topic);
37 | });
38 |
39 | SUBSCRIBER_FIXED_FN_REF_MAP.forEach((functionRef, topic) => {
40 | // attach the function with kafka topic name
41 | this.bindAllTopicToFixedConsumer(functionRef, topic);
42 | });
43 |
44 | await this.fixedConsumer.run({
45 | eachMessage: async ({ topic, partition, message }) => {
46 | const functionRef = SUBSCRIBER_FIXED_FN_REF_MAP.get(topic);
47 | const object = SUBSCRIBER_OBJ_REF_MAP.get(topic);
48 | // bind the subscribed functions to topic
49 | await functionRef.apply(object, [message.value.toString()]);
50 | },
51 | });
52 |
53 | await this.consumer.run({
54 | eachMessage: async ({ topic, partition, message }) => {
55 | const functionRef = SUBSCRIBER_FN_REF_MAP.get(topic);
56 | const object = SUBSCRIBER_OBJ_REF_MAP.get(topic);
57 | // bind the subscribed functions to topic
58 | await functionRef.apply(object, [message.value.toString()]);
59 | },
60 | });
61 | }
62 |
63 | async onModuleDestroy(): Promise {
64 | await this.disconnect();
65 | }
66 |
67 | async connect() {
68 | await this.producer.connect();
69 | await this.consumer.connect();
70 | await this.fixedConsumer.connect();
71 | }
72 |
73 | async disconnect() {
74 | await this.producer.disconnect();
75 | await this.consumer.disconnect();
76 | await this.fixedConsumer.disconnect();
77 | }
78 |
79 | async bindAllTopicToConsumer(callback, _topic) {
80 | await this.consumer.subscribe({ topic: _topic, fromBeginning: false });
81 | }
82 |
83 | async bindAllTopicToFixedConsumer(callback, _topic) {
84 | await this.fixedConsumer.subscribe({ topic: _topic, fromBeginning: false });
85 | }
86 |
87 | async sendMessage(kafkaTopic: string, kafkaMessage: KafkaPayload) {
88 | await this.producer.connect();
89 | const metadata = await this.producer
90 | .send({
91 | topic: kafkaTopic,
92 | messages: [{ value: JSON.stringify(kafkaMessage) }],
93 | })
94 | .catch(e => console.error(e.message, e));
95 | await this.producer.disconnect();
96 | return metadata;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------