You can think of a microservice as a component, which you can develop on its own and install independently. Simple as that. On the other hand, development and maintenance of large apps based on microservices is certainly not simple. There are a few possible indications that might suggest you should split your monolith and use microservices. One of them, for example, is having multiple teams of developers that can work independently on different parts of the app. If you are interested in this topic I would recommend checking out an insightful talk by Matt Ranney from the GOTO Chicago Conference 2016:
In this tutorial I’ll show you how to build a simple microservice in NestJS.
For starters, we will set up a simple monolith app with the use of the Nest CLI, which we will convert to two separate applications. One is a microservice app and the other one is a NestJS client app which is going to consume that microsevice.
On the lookout for a broader overview of Node.js in terms of backend development? Read this article!
Prerequisites - Node.js and Nest CLI
First of all, before you start, you need to have Node.js (version 10.13.0 or higher) installed on your operating system. Install the Nest CLI first using the following command:
npm i -g @nestjs/cli
Create a new Nest project and update dependencies to the latest version.
nest new rocket-app
cd rocket-app
npm i --save @nestjs/core@latest @nestjs/common@latest
Then you can start the app:
npm run start:dev
Once you open your browser and navigate to http://localhost:3000/, you should be able to see the “hello world” welcome message.
Great! Let’s make a quick call to Elon and ask him how long we will need to wait for the next launch of one of his rockets.
For that to happen we are going to create a new service named RocketService and a new controller named RocketController.
cd src && mkdir Rocket && cd Rocket
nest generate service Rocket --flat
nest generate controller Rocket --flat
Now we need to:
- Update the controllers and providers arrays in the app.module.ts.
- Add the HttpModule to imports, as we are going to use it later.
import { Module, HttpModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { RocketService } from './Rocket/rocket.service';
import { RocketController } from './Rocket/rocket.controller';
@Module({
imports: [HttpModule],
controllers: [AppController, RocketController],
providers: [AppService, RocketService],
})
export class AppModule {}
In the rocket.service.ts we will set up a method that will be responsible for fetching data from the SpaceX REST API. We will use the countdown.js npm package to convert the launch date to the remaining time before the launch.
npm i countdownimport { Injectable, HttpService } from '@nestjs/common';
import { map } from 'rxjs/operators';
import * as countdown from 'countdown';
@Injectable()
export class RocketService {
constructor(private http: HttpService) {}
getNextLaunchRemainingTime() {
return this.http.get('https://api.spacexdata.com/v4/launches/next').pipe(
map((response) => response.data),
map((launch) =>
countdown(new Date(), new Date(launch.date_utc)).toString(),
),
);
}
}
Next, we will inject our provider and consume data in the rocket.controller.ts.
import { Controller, Get } from '@nestjs/common';
import { RocketService } from './rocket.service';
@Controller('rocket')
export class RocketController {
constructor(private rocketService: RocketService) {}
@Get('next-launch')
getNextLaunchRemainingTime() {
return this.rocketService.getNextLaunchRemainingTime();
}
}
You should be able to see the result when you open the browser at: http://localhost:3000/rocket/next-launch
Let’s make a copy of the rocket-app directory, and name it “rocket-microservice”.
Converting a monolith to a microservice
First we need to install the required package, inside of the newly created directory.
npm i --save @nestjs/microservices
In the main.ts file we need to change the way of bootstrapping our application.
We are going to use the createMicroservice method which NestJS provides us with in order to create an instance of our microservice application. We are passing along the AppModule as well as the options configuration object. NestJS supports many transport layers out of the box, among others - Redis, MQTT (Message Queuing Telemetry Transport) and RabbitMQ. To keep it simple, we will use the default TCP transport protocol.
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.TCP,
options: {
host: '127.0.0.1',
port: 8877,
},
},
);
app.listen(
() => console.log(
'Microservice is listening on port 8877'
));
}
bootstrap();
In NestJS there are two ways of handling messages - the request-response and event-based method. Which one is better-suited for your case depends, of course, on your business requirements. An event-based approach is a good choice when you want to publish events, without waiting for a response. For example, by notifying that a certain condition has occurred. In this article, I went with the request-response paradigm which is the first choice for exchanging messages between various external services.
In our RocketController currently we are using the Get() decorator function to set it up for http. Instead of that, we now need to decorate it with a @MessagePattern() decorator, which we can import from the @nestjs/microservices package.
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { Observable } from 'rxjs';
import { RocketService } from './rocket.service';
@Controller('rocket')
export class RocketController {
constructor(private rocketService: RocketService) { }
@MessagePattern('get-next-launch-remaining-time')
getNextLaunchRemainingTime(): Observable<string> {
return this.rocketService.getNextLaunchRemainingTime();
}
}
Well done - we’ve successfully created a microservice. But wouldn't it be great if we could try it out? That’s why we are going to set up the NestJS client app which will exchange messages with our microservice using the ClientProxy class.
All caught up in NestJS? Check out this article about typed and validated REST API using NestJS!
NestJS Client app
Let’s convert our monolith app in the rocket-app directory to our Nest Client app. As previously, we need to add the required Nest microservices package.
npm i --save @nestjs/microservices
All we need to do now is to configure our RocketService to send a message to our microservice and return the result as an Rx Observable stream.
To simplify things for the sake of this example, I’ve created an instance of the client directly inside the RocketService.
import { Injectable } from '@nestjs/common';
import { ClientProxyFactory, Transport, ClientProxy } from '@nestjs/microservices';
@Injectable()
export class RocketService {
client: ClientProxy;
constructor() {
this.client = ClientProxyFactory.create({
transport: Transport.TCP,
options: {
host: '127.0.0.1',
port: 8877,
},
});
}
getNextLaunchRemainingTime() {
return this.client
.send<string, string>(
'get-next-launch-remaining-time', '',
);
}
}
With the help of the ClientProxyFactory we created a proxy that was assigned to the client property of the Rocket Service.
Inside the getNextLaunchRemainingTime function we call the send method on our client proxy object. The first parameter is the pattern (could be an object or a string) that we set before in our microservice app rocket controller. The second parameter is the body of our message, in our case it’s just an empty string.
Now everything is ready. Make sure both apps are up and running and hit on http://localhost:3000/rocket/next-launch. Our Nest Client app had used our microservice app to retrieve the data from the SpaceX RestApi.
Up for something new?
Summing up, this article provided you with a teaser on how to create a simple microservices-based application. Try it yourself and see how it goes! We hope you enjoyed this tutorial and that we got you interested in learning more about this approach.
Want to use NestJS apps in practice? Join our smashing NodeJS backend team!
Navigate the changing IT landscape
Some highlighted content that we want to draw attention to to link to our other resources. It usually contains a link .