Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NestJS NATS request-response

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.

like image 493
rhlsthrm Avatar asked Jun 11 '19 18:06

rhlsthrm


Video Answer


2 Answers

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"}' 
} }
like image 71
Kim Kern Avatar answered Oct 01 '22 12:10

Kim Kern


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();
like image 41
ss1080 Avatar answered Oct 01 '22 13:10

ss1080