01. Getting started with Nest.js
Exploring the Benefits of Using Nest.js for Building Scalable and Modular Microservices
The trend of building applications using a microservices architecture is gaining traction in the world of modern software development. Microservices are small, independent components that work together to form an application. This approach has many advantages over traditional monolithic architecture, including improved scalability, flexibility, and maintainability.
If you're interested in building microservices using Node.js, you're in the right place. Node.js is a popular platform for building scalable and high-performance applications, and it's well-suited for creating microservices. In this blog post, we'll explore the basics of microservices architecture, discuss the benefits of using Node.js for microservices, and walk you through the steps of building microservices using Node.js. We will learn all of this by building our blogging application, which will have basic authentication, user management, and, of course, blog posting. We will also set up a CI/CD pipeline using GitLab, including our own private NPM module hosted on the GitLab package registry.
๐ค Why Nest.js?
When it comes to choosing a framework for building microservices using Node.js, Nest.js is a popular and highly recommended choice. Nest.js is a progressive framework that builds on top of the widely-used Express.js library and provides a robust set of features and functionality for building scalable and modular applications. With its focus on TypeScript, Nest.js makes it easier to write maintainable and well-structured code, which is essential when working with microservices.
Additionally, Nest.js provides a powerful CLI that makes it simple to generate boilerplate code and set up the project structure, allowing developers to get up and running quickly. Finally, Nest.js comes with built-in support for a range of commonly used libraries and tools, such as GraphQL and WebSocket, and in our case, it comes with support for building microservices, making it a versatile choice for building microservices. We will also set up a basic nginx reverse proxy for handling API requests and transferring them to the respective microservice.
๐ What we are going to build?
We're going to build a blog application consisting of three microservices: user, auth, and post. These microservices will run independently within a Docker container, while nginx will act as a reverse proxy for handling API requests. To store user data, we'll initially use an array, but we may replace this with an actual database in the future. We will also use Docker for creating images for our microservices. And lastly, for demonstration purposes, we will create an NPM package and host it on the GitLab Package Registry. Keep an eye out for potential articles on this topic!
๐ Prerequisites
Before diving into building microservices using Nest.js, there are a few prerequisites you should have in place.
Solid understanding of JavaScript and Node.js
Basic knowledge of TypeScript
Experience working with REST APIs
General understanding of microservices architecture
Node.js and the Nest.js CLI installed on your local machine
Familiarity with the Nest.js framework, which can be gained through the official documentation
Click here to learn how to install the Nest.js CLI on your machine.
Step 1: ๐ Setting up the Nest.js project
We will start by creating a folder called blog-app
and setting up our first service, called user
. Run the following command in the terminal.
mkdir blog-app
cd blog-app
nest new user
For the package manager, I chose "pnpm," but you could go with any other manager you like.
Next, we will open the project inside the VS Code editor using code .
command, you can use any other editor or IDE you like. Your project will look something like this
Step 2: ๐ Writing services for "user microservice"
๐ You might be confused by the title; well, let me explain. We are going to write services for our "user microservice." Services in Nest JS are injectable functions that can be called from other services or controllers.
Now that we have our "user" service set up, we will write two functions in the service file for creating and finding users. Later on, we will call this function via TCP from other services.
Open up the src/app.service.ts
file in the editor, and write the following code:
import { Injectable } from '@nestjs/common';
interface User {
id: number;
username: string;
password: string;
}
const users: User[] = [];
@Injectable()
export class AppService {
createUser(username: string, password: string): User {
const id = users.length + 1;
const user = {
id,
username,
password, // Dont' store password in plain text in production
};
users.push(user);
return user;
}
getUser(id: number): User | null {
return users.find((user) => user.id === id);
}
}
We created two functions createUser()
and getUser()
for creating and getting the user into the app service.
Note: Please note that for demonstration purposes, we are currently storing user data in an array along with plain-text passwords. However, in a production environment, it is important to use a separate database to store user information and employ password encryption techniques to ensure the secure handling of sensitive data.
Step 3: โ๏ธ Writing controllers for "user microservice"
Controllers are responsible for handling incoming requests and returning responses to the client. In our current implementation, we have created an HTTP controller, which will be replaced with a TCP controller to handle requests from other microservices like the "auth microservice" in the future. It is important to ensure that clients do not directly access the "user microservice" and instead make an HTTP call to the "auth microservice" for registering a new account. For the purpose of this discussion, "client" refers to someone who is using our API.
We will create two controllers, one for creating users and the other for fetching user data using id
from the query. Here is the code for app.controller.ts
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { AppService, User } from './app.service';
@Controller('/users')
export class AppController {
constructor(private readonly appService: AppService) {}
@Post()
createUser(@Body() user: User) {
// we are calling the createUser method from app.service.ts
return this.appService.createUser(user.username, user.password);
}
@Get(':id')
getUser(@Param() id: number) {
return this.appService.getUser(id);
}
}
In the getUser()
function, we are getting the user ID from the URL. Example:domain.com/users/1
have user ID "1."
Now, let's test the user microservice by spinning it up via Nest CLI, Run the following command in the terminal:
cd user
pnpm run start:dev
Next, we will make a POST and a GET request to test our controller. You can use Postman (or Insomnia) for this.
As you can see, we get the response from the server with the ID of the user we just created. Now let's test the other function to see if we are getting the user details by using the ID.
That's working :)
In an upcoming blog post, we'll explore the conversion of our user microservice application from HTTP to the TCP protocol, enabling communication with other microservices. Additionally, we'll create a separate authentication microservice that will use the user microservice for creating and fetching users.
Let me know in the comment or reach out to me via Twitter if you get any issues following my steps. This is my early stage in writing blogs, so do let me know if you have any suggestions regarding the blog. Peace โ๏ธ