最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - NestJS NATS request-response - Stack Overflow

programmeradmin1浏览0评论

I'm trying to use NestJS and the NATS microservice. There is good documentation for setting up a basic request-response.

What I did is the following:

Ran a local NATS server.

Set up my main.ts to connect to the server:

async function bootstrap() {
  const app = await NestFactory.createMicroservice(AppModule, {
    options: {
      url: "nats://localhost:4222",
    },
    transport: Transport.NATS,
  });
  app.listen(() => console.log("Microservice is listening"));
}
bootstrap();

Created a ClientProxyFactory to send back messages:

export const NatsClientProvider: Provider = {
  inject: [ConfigService],
  provide: NatsClientProviderId,
  useFactory: async (config: ConfigService) =>
    ClientProxyFactory.create({
      options: {
        servers: config.getNatsConfig().servers,
      },
      transport: Transport.NATS,
    }),
};

Set up a controller app.controller.ts to respond to a certain pattern:

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    @Inject(NatsClientProviderId) private readonly natsClient: ClientProxy,
  ) {}

  @MessagePattern("hello")
  async getHello(data: string) {
    console.log("data: ", data);
    console.log("getHello!!");
    await this.natsClient.send("hello", this.appService.getHello());
    return this.appService.getHello();
  }

  async onModuleInit() {
    await this.natsClient.connect();
    console.log("Nats connected!");
  }

Set up a test file to try sending a request-response message:

import { connect } from "ts-nats";

async function start() {
  const nc = await connect({
    servers: ["nats://localhost:4222"],
  });

  const msg = await nc.request("hello", 5000, "me");
  console.log("msg: ", msg);
}

start();

When I run my Nest app, I can see the subscription created properly in the NATS server logs.

When I run the test.ts file, it times out with NatsError: Request timed out.. However, I can see my console logs (although the data is undefined even though I am specifying it in the published message.

Neither the return nor the client.send methods are working to receive messages back from the app.

Any help is appreciated!

EDIT: Still looking into and stuck on this issue. In the "Sending Messages" section of the Microservice docs, it says "The pattern has to be equal to this one defined in the @MessagePattern() decorator while payload is a message that we want to transmit to another microservice.". If I do that, the Nest app detects the message it sends and gets stuck in an infinite loop of sending a message and receiving the same message back and forth to itself forever.

I'm trying to use NestJS and the NATS microservice. There is good documentation for setting up a basic request-response.

What I did is the following:

Ran a local NATS server.

Set up my main.ts to connect to the server:

async function bootstrap() {
  const app = await NestFactory.createMicroservice(AppModule, {
    options: {
      url: "nats://localhost:4222",
    },
    transport: Transport.NATS,
  });
  app.listen(() => console.log("Microservice is listening"));
}
bootstrap();

Created a ClientProxyFactory to send back messages:

export const NatsClientProvider: Provider = {
  inject: [ConfigService],
  provide: NatsClientProviderId,
  useFactory: async (config: ConfigService) =>
    ClientProxyFactory.create({
      options: {
        servers: config.getNatsConfig().servers,
      },
      transport: Transport.NATS,
    }),
};

Set up a controller app.controller.ts to respond to a certain pattern:

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    @Inject(NatsClientProviderId) private readonly natsClient: ClientProxy,
  ) {}

  @MessagePattern("hello")
  async getHello(data: string) {
    console.log("data: ", data);
    console.log("getHello!!");
    await this.natsClient.send("hello", this.appService.getHello());
    return this.appService.getHello();
  }

  async onModuleInit() {
    await this.natsClient.connect();
    console.log("Nats connected!");
  }

Set up a test file to try sending a request-response message:

import { connect } from "ts-nats";

async function start() {
  const nc = await connect({
    servers: ["nats://localhost:4222"],
  });

  const msg = await nc.request("hello", 5000, "me");
  console.log("msg: ", msg);
}

start();

When I run my Nest app, I can see the subscription created properly in the NATS server logs.

When I run the test.ts file, it times out with NatsError: Request timed out.. However, I can see my console logs (although the data is undefined even though I am specifying it in the published message.

Neither the return nor the client.send methods are working to receive messages back from the app.

Any help is appreciated!

EDIT: Still looking into and stuck on this issue. In the "Sending Messages" section of the Microservice docs, it says "The pattern has to be equal to this one defined in the @MessagePattern() decorator while payload is a message that we want to transmit to another microservice.". If I do that, the Nest app detects the message it sends and gets stuck in an infinite loop of sending a message and receiving the same message back and forth to itself forever.

Share Improve this question edited Jun 14, 2019 at 13:18 Kim Kern 60.4k20 gold badges216 silver badges213 bronze badges asked Jun 11, 2019 at 18:24 rhlsthrmrhlsthrm 8611 gold badge13 silver badges25 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 7 +50

To avoid the infinite loop in your controller, remove the natsClient.send statement. MessagePattern will automatically send a reply with the data you return from the function, in your case this.appService.getHello():

@MessagePattern("hello")
async getHello(data: string) {
  console.log("data: ", data);
  return "Hello World!";
}

Nest requires you to send a long an id attribute (any string is fine) for it to be able to reply to a message. Just include it in the data json:

// Nest expects the data to have the following structure
const reply = await nc.request("hello", 500, JSON.stringify({ data: "Hello", id: "myid" }));
console.log({ reply });

In your nest log, you'll see the following log entry:

data: Hello

In your test script, you'll see this:

{ reply:
   { subject: '_INBOX.GJGL6RJFYXKMCF8CWXO0HB.GJGL6RJFYXKMCF8CWXO0B5',
     sid: 1,
     reply: undefined,
     size: 50,
     data: '{"err":null,"response":"Hello World!","id":"myid"}' 
} }

When using the ClientProxy, send and emit return Observables. You need to "activate" those for them to do anything. So you can either subscribe to them, or change it to a Promise.

since you are using await you probably want to do

await this.natsClient.send("hello", this.appService.getHello()).toPromise();

Nest Nats suuports two way out from box, you just need to configure MessagePattern https://docs.nestjs.com/microservices/basics#request-response

// service 1: data-source controller

@Controller()
export class AppController {
  @MessagePattern('data-source.status')
  async status() {
    return 'Ok!';
  }
}
// service 2 somewere in the code... 

import { firstValueFrom } from 'rxjs';
...
const req = this.nats.send('data-source.status', {}); // Observable
const res = await firstValueFrom(req).catch((e) => console.error(e));
console.log({ res });
...
// out
{ res: 'Ok!' }
// both services has same NATS configuration

const logger = new Logger();

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      logger: logger,
      transport: Transport.NATS,
      options: {
        servers: process.env.NATS,
      },
    },
  );
  await app.listen();
}

bootstrap();
// and NATS provider to inject in services and send requests...

import { Provider } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ClientProxyFactory, Transport } from '@nestjs/microservices';

export const NATS = 'NATS_CLIENT';

export const NatsProvider: Provider = {
  provide: NATS,
  useFactory: (configService: ConfigService) => {
    return ClientProxyFactory.create({
      transport: Transport.NATS,
      options: {
        url: configService.get('NATS'),
      },
    });
  },
  inject: [ConfigService],
};

发布评论

评论列表(0)

  1. 暂无评论