docs.nestjs.com icon indicating copy to clipboard operation
docs.nestjs.com copied to clipboard

Microservices interaction with external services

Open purplefish32 opened this issue 5 years ago • 18 comments

I'm submitting a...


[ ] Regression 
[X ] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

When using nestjs as a NATS microservice, the nats payload (data), when using the @MessagePattern decorator, is always undefined.

  @MessagePattern('sms.send')
  test(data: any) {
    console.log('function called OK');
    console.log(data); // Allways undefined
  }

In the provided example I can log "function called OK" but data is allways undefined.

Here is my main.ts

import { NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';

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

I have tried both regular strings and json encoded objects as valid payloads.

Expected behavior

data should not be undefined

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Environment


Nest version: X.Y.Z

 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

purplefish32 avatar May 06 '19 14:05 purplefish32

Please, provide a minimal repository which reproduces your issue.

kamilmysliwiec avatar May 06 '19 14:05 kamilmysliwiec

Here you are, it is a verry basic example. https://github.com/purplefish32/nest-nats instructions are in the README file. By the way I used nest cli to generate the app.

purplefish32 avatar May 06 '19 16:05 purplefish32

The @EventPattern decorator, has exactly the same problem, I have updated my repo with an example.

purplefish32 avatar May 07 '19 08:05 purplefish32

A Ha!

I am on to something: if I change my simple nats emitter from

nats.publish('sms.send', JSON.stringify({ message: "JSON payload!" }));

to

nats.publish('sms.send', JSON.stringify({ data: "JSON payload!" }));

I recieve the proper paload.

This is both undocumented and IMHO not the correct way to handle a NATS payload. A NATS payload can be any type of string, not necesairily JSON, and the JSON can be any format.

My payloads are usualy formated like this :

{ "entity": "..." }

purplefish32 avatar May 07 '19 09:05 purplefish32

@kamilmysliwiec I would love to provide a pull request to fix this issue, but I am a Lerna noob and have no idea how to get the package.json of my test repo to use @nest/microservices from my fork instead of the one published on npm.

Is there a way to point directly to my github or do I have to use a private npm repo to publish my changes ?

purplefish32 avatar May 07 '19 17:05 purplefish32

Nest wraps every message exchanged by microservices with its own structure understandable by the framework (hence, data property is required). As far as I understand, you want to communicate with Nest microservice using an external service that is not built on top of Nest. We need to provide better docs for that, thanks for pointing this out.

kamilmysliwiec avatar May 09 '19 07:05 kamilmysliwiec

I dont think it is a documentation issue, I think is the way that Nest microservices communicate via NATS is incorrect in that a NATS payload should be by default an arbitairy string not an opiniated JSON object.

purplefish32 avatar May 09 '19 09:05 purplefish32

IMHO, the nats-server should be initialised as a sting by default, or JSON via a new option

example:

const app = await NestFactory.createMicroservice(ApplicationModule, {
  transport: Transport.NATS,
  options: {
    url: 'nats://localhost:4222',
    json: true // New option
  },
});

And in the case of a JSON paload : data used in @MessagePattern and @EventPattern should be the root object of the JSON payload, not the object.data

@MessagePattern('sms.send')
  test(data: {} : null {
    console.log(data)
  }

I hope I am clear in my explanaition :)

Otherwise It means that every other non Nest NATS serivce must conform to the nestjs spec. which is a shame.

purplefish32 avatar May 09 '19 09:05 purplefish32

Nest needs to use particular JSON shape in order to stay cohesive between other transport layers. This JS object is serialized to string (stringified JSON) under the hood and sent through the network.

Otherwise It means that every other non Nest NATS serivce must conform to the nestjs spec. which is a shame.

Yes, this is exactly what it means and I don't think this is "a shame".

IMHO, the nats-server should be initialised as a sting by default, or JSON via a new option

This is something that we could potentially add in the future

kamilmysliwiec avatar May 09 '19 09:05 kamilmysliwiec

@kamilmysliwiec

Are there any plans to allow custom shape? I'd like to migrate our services to NestJS, but we currently use this shape:

{
  type,          // Equivalent of NestJS "pattern"
  appId,         // Some data specific to our platform.
  loginUserId,   // Some data specific to our platform.
  payload,       // Equivalent of NestJS "data"
}

So far, this is really the only hurdle for our migration to NestJS. Awesome framework!

[EDIT]

Is there a way to add translation logic for messages? I tried using an Interceptor, but undefined is the only element in the context.args array. Looks like Nest is pulling out the data property before the Interceptor is called.

amh4r avatar Jun 16 '19 15:06 amh4r

@goodoldneon what about passing your custom shape inside NestJS data? We need pattern to associate messages with handlers. You could then add interceptor that plucks data to avoid passing appId, type, and loginUserId to handler.

kamilmysliwiec avatar Jun 17 '19 21:06 kamilmysliwiec

The problem is that our events are sent with the payload property, rather than data. Consequently, data is undefined in my MessageBrokerController.

I added an interceptor to rename the payload property to data, but context.args[0] is undefined. It seems as though there isn't a way to intercept the event before NestJS extracts data.

Eventually I want to migrate all of our services to NestJS (since NestJS is awesome ❤️), but in the meantime I need to add a layer that translates our existing event shape into the shape NestJS expects (pattern and data). I'm totally OK with NestJS being opinionated for event shape; I just need a way to make existing events adhere to NestJS's expectation.

amh4r avatar Jun 18 '19 01:06 amh4r

@kamilmysliwiec any updates regarding laravel & nest integration using microservices ? :)

bcba25 avatar Jul 01 '19 08:07 bcba25

Feature is almost finished https://github.com/nestjs/nest/pull/2653

kamilmysliwiec avatar Jul 31 '19 14:07 kamilmysliwiec

@kamilmysliwiec any ETA for the docs? :D

gperdomor avatar Oct 01 '19 20:10 gperdomor

This article discusses the topic of serialization in depth. It focuses largely on NATS, but much of the discussion applies to all transporters: https://dev.to/nestjs/integrate-nestjs-with-external-services-using-microservice-transporters-part-1-p3

This article may also be of interest: https://dev.to/nestjs/part-1-introduction-and-setup-1a2l

johnbiundo avatar Mar 02 '20 18:03 johnbiundo

Nest wraps every message exchanged by microservices with its own structure understandable by the framework (hence, data property is required). As far as I understand, you want to communicate with Nest microservice using an external service that is not built on top of Nest. We need to provide better docs for that, thanks for pointing this out. @kamilmysliwiec

Stumbled on the same issue where nest should act with external parties over a message queue and it took me quite a while to figure out how to use the @EventPattern() and @MessagePattern() decorators. +1 for adding a part in the documentation which goes over the expected data structure parsed by the mentioned decorators.

tizzyapunkt avatar Mar 28 '23 14:03 tizzyapunkt

FYI i faced this issue, but fixed it by making my controller the event handler, instead of my service.

montera82 avatar Mar 18 '24 08:03 montera82