Mastering Microservices: A Comprehensive Guide to Building an API with NestJS - Part 1
Part 1 of our series on microservices with NestJS: Discover the basics & advantages of microservices over traditional architectures. Explore setup, dependencies, and development process using NestJS & RabbitMQ. Stay tuned for more on building a comprehensive microservices app.
When software development was still in its early stages, there were monolith codebases that had code intermingled all over the place. Separation of concerns wasn't an issue and was difficult to keep up. Then came the n-tier architecture, which divided code into functional areas. Without causing significant damage, different code components could be changed.
For instance, the database layer would solely be concerned with communicating with the database, not the application, view, or other layers. As a result, the codebase was maintainable. However, the issue of scalability emerged following the internet explosion. Microservice architecture can be used in this situation.
What are Microservices?
Microservices takes the separation of concern from n-tier architecture to the next level. Microservices are independent applications that exist on their own. In most cases, they have their own database, which is isolated from others. They are hosted separately and are loosely coupled. They communicate with each other by using communication means such as API calls or message passing. They can be scaled, maintained and upgraded independently. This also means that different teams can work on their own microservice in different languages. Each microservice can be based on the n-tier architecture as well.
💡
There is also a messaging layer present between microservices and the presentation layer, which is used for inter-microservice communications. I skipped that part in the diagram for simplicity.
Building Microservices In NestJS
NestJS supports building applications with microservice architecture out of the box. It even supports various message communication strategies like gRPC, and MQTT. If we pair it with Nx, we will have a solid tool for developing our application with multiple microservices.
Setting Up Environment
Creating Nx Workspace
Let us create a dummy authentication service project using microservices with NestJS and RabbitMQ. Let's start by setting up our Nx workspace. Run the following commands in your terminal emulator:
npx create-nx-workspace@latest --pm=pnpm
When prompted for the workspace name, type payment-app and hit enter. For the stack, choose node , and for the framework, choose NestJs. In the next prompt, select integrated Monorepo. Enter api-gateway when prompted for the app name. Select no for Dockerfile and Nx cloud. The end result should look like something shown in the screenshot below.
Installing Dependencies
We will need to install some additional dependencies as well. Run the following commands.
Setting .env File
We will be using an .env file to manage development environment variables. Create a file in the project root and name it .env. Fill the content of the file as shown below
Composing a Docker File
Let's set up our docker-compose file. We will only be running one service in it, rabbitmq. Create a docker-compose.yml file in the root of your project.
Run docker compose up -d to start the service.
Creating a Shared Library
We will create a shared library containing code that'll be shared between the microservices like DTOs, configs, etc. Run the following command in the root of the project. A new library should be generated in the libs directory.
We will add the DTOs and Entities. Create a dtos folder and an entity folder in the shared library's libs folder. Create the files auth.dto.ts and index.ts in dtos and index.tsand user.entity.ts in the entities folder and populate as follows.
Ultimately, we will export them all in the shared library's barrel file.
Update the main.ts file in our API-gateway project as follows.
Creating the Auth Microservice
Now, let us create our Auth microservice. Run the following command in the project root.
Populate the main.ts as follows.
Here, the auth microservices are connected to our RMQ server and ready to Pub/Sub messages in the AUTH_QUEUE.
Create auth.controller.ts, auth.service.ts, auth.module.ts and user.repository.ts in the app folder of auth-microservice. Then, populate them with the following.
For simplicity, we will be using an in-memory database instead of an actual database in user.repository.ts.
In our API-gateway application, create three new files auth.controller.ts, auth.service.ts and auth.module.ts in a folder named auth in the app directory and populate the following.
Add AuthModule in the app.module.ts. The AuthModule In API gateway is connected to the out RMQ'sAUTH_QUEUE. The gateway will communicate with the auth microservice through this channel. Here in AuthService's createUser method, we can see that it sends the CREATE_USER message with a payload of type CreateUserDTO. This is then handled by the corresponding MessageHandler which we described in the auth microservice, which is handleUserCreate method in auth microservice. It then responds with the newly created User entity to the api-gateway.
Testing Auth API
Open the API testing application of your choice. Here, I'll be using Insomnia and making a POST request to the endpoint /api/auth/sign-up as following.
Here, the API gateway creates and passes the incoming request to the AUTH_QUEUE message queue and the auth microservice creates the user in the "database" and then returns the created user data, which is returned to the caller.
Conclusion
In this part, we implemented the auth microservice and connected it to our gateway through RabbitMQ. We connected our gateway and auth microservice through RabbitMQ. In the next part, we will implement a Payment microservice. We will also learn how to handle errors in microservices in NestJs.
Thank you for reading this article. If you loved it, please consider subscribing and leaving a response in the comment section below.