How to split Nest.js microservices into separate projects?

Tomasz Gałkowski picture Tomasz Gałkowski · Sep 11, 2018 · Viewed 10.5k times · Source

Let's say I want to create a simplistic cinema-management platform. It needs few microservices: movies, cinemas, payments, etc.

How would you go about doing it in Nest.js? I don't want them in the same big folder as that feels like making a monolith. I want them to be separate Nest.js projects with their own git repositories so I can orchestrate them with Kubernetes later on.

How? How to connect from service cinemas to service movies if they are two separate projects and only share, let's say, Redis?

Edit: This is not a question about microservices in general. This is a question Nest.js specific. I read the documentation, I know there are decorators like @Client for connecting to the transport layer. I just want to know where to use that decorator and maybe see a short snippet of code on "having two separate Nest.js repositories how to connect them together so they can talk to each other".

I don't care about the transport layer, that thing I can figure out myself. I just need some advice on the framework itself as I believe the documentation is lacking.

Answer

Tomasz Gałkowski picture Tomasz Gałkowski · Sep 13, 2018

I got it working. Basically the way to do it is to create two separate projects. Let's say - one is a createMicroservice and another is just an HTTP app (but could easily be another microservice). I used a "normal" app just so I can call it easily for testing.

Here is the main.ts file that creates microservice.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Transport } from '@nestjs/common/enums/transport.enum';

async function bootstrap() {
  const app = await NestFactory.createMicroservice(AppModule, {
    transport: Transport.REDIS,
    options: {
      url: 'redis://localhost:6379',
    },
  });
  await app.listen(() => console.log('MoviesService is running.'));
}
bootstrap();

And one of the controllers:

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @MessagePattern({ cmd: 'LIST_MOVIES' })
  listMovies(): string[] {
    return ['Pulp Fiction', 'Blade Runner', 'Hatred'];
  }
}

Now - in the microservice you declare to what kinds of events should controllers react to (@MessagePattern). While in the "normal" service you do this in the controller when you want to ask other microservices for something (the main.ts is the simplest example that you get when you create a new project using @nestjs/cli.

The controller code:

@Controller()
export class AppController {
  private readonly client: ClientProxy;

  constructor(private readonly appService: AppService) {
    this.client = ClientProxyFactory.create({
      transport: Transport.REDIS,
      options: {
        url: 'redis://localhost:6379',
      },
    });
  }

  @Get()
  listMovies() {
    const pattern = { cmd: 'LIST_MOVIES' };

    return this.client.send<string[]>(pattern, []);
  }
}

So as long a client is connected to the same transport layer as the microservice - they can talk to each other by using the @MessagePattern.

For nicer code you can move the this.client part from a constructor to a provider and then use dependency injection by declaring the provider in the module.