loading...
Cover image for Monorepo and Microservice setup in Nest.js

Monorepo and Microservice setup in Nest.js

lampewebdev profile image Michael "lampe" Lazarski Updated on ・5 min read

NestJS (3 Part Series)

1) Monorepo and Microservice setup in Nest.js 2) NestJS - Microservices with Redis 3) NestJS - Adding a frontend to the monorepo

Let's first define what a monorepo is

a monorepo (a syllabic abbreviation of a monolithic repository) is a software development strategy where code for many projects is stored in the same repository.

Instead of heaving a git repo for every service or app, we will just have one git repo where everything will be included.

This style of organizing your code has some benefits.
Code reusability is easy since the code is already there. Updating dependencies is also more comfortable when you set up your mono repo correctly.

Now let's define what are 'microservices'.

Microservices is a software development technique. A variant of the service-oriented architecture (SOA) structural style— that arranges an application as a collection of loosely coupled services. In a microservices architecture, services are fine-grained, and the protocols are lightweight.

Instead of having everything in one application/service, we have minimal independent services. This can mean that every service has its database, can be written in a different programming language, and also should be easy to replace. Microservices are a hot and significant topic. Like everything, they have cons and pros! If you want to know more about microservices, write it down below in the comments, and I can write more about them in the next blog post!

Monorepo in nestjs

First, we need to install the Nest CLI.

npm install -g @nestjs/cli
or
yarn global add @nestjs/cli

Now you should have the nest command in your terminal.
We can test that with:

nest --version

Now we can create a "standard" project with

nest new lampeweb

You can either choose if you want to use npm or yarn. Since I like to use yarn, I always select yarn.

Now that we have created a standard project, we need to turn this project into a monorepo. This is pretty simple with nest we just need to run the following command:

cd lampeweb
nest generate app blog

That's it! Now we have a monorepo. As you can see, the src folder is gone, and we now have an apps folder. In the apps folder, we can now find both of our applications/microservices.

One important file is the nest-cli.json file. When you open that file, you can see a JSON configuration file with a lot of entries. The import entries for us are "root": "apps/lampeweb",. This entry tells the Nest CLI where the main file of that project is. Also, you can find the "projects": { entry. Here we can find a list of every app/service in that project.

Before we can do that, we need to change the port of our blog app.

open apps/blog/src/main.ts and change the following line:

await app.listen(3000);

to

await app.listen(4000);

now let's start our services.

nest start

and in a second terminal

nest start blog

So the first command will start our root app. In our case, this is the lampeweb app. The second command will start the blog service. Easy, right?

Now we have two apps running in a mono repo!

Microservices

The first thing we have to do is to add the Nest microservice package to our project.

yarn add @nestjs/microservices

First, we need to edit apps/blog/src/main.ts

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

const logger = new Logger('Blog');

async function bootstrap() {
  const app = await NestFactory.createMicroservice(AppModule, {
    transport: Transport.TCP,
    options: {
      port: 4000,
    },
  });
  await app.listen(() => logger.log('Microservice is listening'));
}
bootstrap();

We changed NestFactory.create to NestFactory.createMicroservice. This will tell Nest that this app now is a microservice. We now also have a configuration JSON. We need to tell Nest what transport method we want to use. TCP is the simplest one and does not need any extras. We can also use Redis, RabbitMQ, and many more. If there is enough interest in this article, then I can go into more detail about that topic. We also need to add the port to the configuration JSON.

The second file in that app/service/microservice we need to edit is apps/blog/src/app.controller.ts.

We need to change

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

to

  @MessagePattern('getHello')
  getHello(name: string): string {
    return this.appService.getHello(name);
  }

Now we don't have an http verb anymore but MessagePattern. With the 'getHello' name we can later call that function.

The third file we want to change is apps/blog/src/app.service.ts
We need to change

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

to

@Injectable()
export class AppService {
  getHello(name: string): string {
    return `Hello ${name}!`;
  }
}

So that this getHello message accepts a string so that we can return the name back.
That's it! Our blog microservice is done.

In our lampeweb app, we need to change the following file apps/lampeweb/src/app.service.ts.
From

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

to

import { Injectable } from '@nestjs/common';
import {
  ClientProxyFactory,
  Transport,
  ClientProxy,
} from '@nestjs/microservices';

@Injectable()
export class AppService {
  private client: ClientProxy;

  constructor() {
    this.client = ClientProxyFactory.create({
      transport: Transport.TCP,
      options: {
        port: 4000,
      },
    });
  }

  public getHello(): Promise<string> {
    return this.client.send<string, string>('getHello', 'Michael').toPromise();
  }
}

Okay, this looks like a lot! We added a constructor method that created our client. We need to tell our client what transport and what port the microservice we want to connect to uses. In a real-world app, you also need to provide the host, but we are skipping that here because this runs on the same machine.

We now just need to modify our getHello method to return a promise. The send method takes two parameters. The first one is the name of the message we are sending, and the second one is the payload we want to send. In our case, just a simple string with a name.

The next file we need to change is apps/lampeweb/src/app.controller.ts from

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

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

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

to

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

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

  @Get()
  async getHello(): Promise<string> {
    const helloValue = await this.appService.getHello();
    return helloValue;
  }
}

Not much has changed just that we are now expecting a promise to be resolved to a string.

That's it!
Now you need to restart both services:

nest start
#and
nest start blog

If you want, you can also add the --watch flag to both commands so nest will rebuild the service every time you save.

Now we just need to open a browser and go to http://localhost:3000/.

You should now see the following
Alt Text

I hope you liked that post! If you want a follow-up, please comment, like, and share. So I can know that you are interested in content like that!

👋Say Hello! Instagram | Twitter | LinkedIn | Medium | Twitch | YouTube

NestJS (3 Part Series)

1) Monorepo and Microservice setup in Nest.js 2) NestJS - Microservices with Redis 3) NestJS - Adding a frontend to the monorepo

Posted on by:

lampewebdev profile

Michael "lampe" Lazarski

@lampewebdev

I'm a full-stack web developer. I love to help people.

Discussion

markdown guide
 

I know this will be a lot of work but it would be awesome if you could make a tutorial with a lot more complex example.

Currently I didn't find a good tutorial about:

  • DDD
  • CQRS
  • Event Sourcing
  • Microservices

all together :)

 
 
 

That sounds like a bigger project :D

I will think about it

specially DDD is not that easy to explain in a short blog post but I will thinking about it :)

 
 

Nice Article!

I have Nest microservices in different repos. Planning to use monorepo, but one thing struck my mind which is, each app under src don't have package.json, unlike lerna + yarn workspace.

Then, how will docker work? Let's say microserviceA has dependencies 1,2,3 and microserviceB has dep 4,5. With this setup, microserviceA container will have all the dependencies inside and same for B.

 

I think will need to use lerna or yarn workspace.

According to this: github.com/nestjs/nest-cli/issues/533

'The philosophy behind Nest CLI monorepo is different (it assumes that all shared libraries will exist in one repository, won't be published to any registry, and will be bundled together with the app).'

 

Great content!
I'm starting with NestJS, reading your content I'm developing a NestJS monorepo with microservices and adding custom shared libraries.
I cannot found informtation about how to build monorepo for production.
My shared libraries only works in root app (my Gateway) from my monorepo, but un my another apps (auth and API microservices) it Is not found.
Someone has a similar issues?
Regards
More description about my problems here:
github.com/nestjs/nest/issues/4984

 

I really like nest.js (due to a lot of reasons) but does it provide templating? or can i only make a decoupled backend with it?

 

you can create an angular frontend with the nest cli.

the rest needs to be implemented manualy as far as i know.

 

Great article. Can You write more about alternative transport layers (Redis, RabbitMQ) and more about deploy nestjs apps and nestjs microservices on production? :)

 

Sure :) I will start soon on the other transport layers.

Still figuring out how to containerize the mini repo but once that is done there will be an article.

Also thinking about video versions