<link rel="stylesheet" href="styles-3GBANQL6.css">
← Back to blog

NestJS

Building Microservices with NestJS: A Real-World Example

Building Microservices with NestJS: A Real-World Example

Introduction to NestJS Microservices

Microservices architecture has gained immense popularity in recent years due to its flexibility, scalability, and ability to foster continuous deployment. NestJS, a progressive Node.js framework, provides excellent support for building microservices. It allows developers to create modular, maintainable applications that can grow and scale independently.

In this blog post, we will explore how to build a microservices application using NestJS with a real-world example. We’ll cover the basics of NestJS microservices, set up a simple project, and implement a service that communicates between multiple microservices using Kafka.

Understanding NestJS Microservices

NestJS provides an easy way to create microservices that can communicate with each other using different transport layers such as TCP, Redis, NATS, and Kafka. The framework allows you to define microservices with minimal boilerplate code, making it easy to focus on business logic.

Key Features:

  • Modularity: Each microservice can be developed, tested, and deployed independently.
  • Scalability: Services can be scaled based on demand.
  • Inter-Service Communication: NestJS provides various options for communication between services, including message brokers like Kafka.

Setting Up a NestJS Microservices Project

Prerequisites

  • Node.js installed on your machine
  • Basic understanding of TypeScript and NestJS
  • Apache Kafka installed or a cloud provider (e.g., Confluent Cloud) with access to a Kafka instance

Step 1: Creating a New NestJS Project

First, install the Nest CLI if you haven’t already:

npm i -g @nestjs/cli

Create a new project:

nest new nest-microservices
cd nest-microservices

Step 2: Installing Required Packages

We will use @nestjs/microservices and kafkajs for Kafka support. Install them using npm:

npm install @nestjs/microservices kafkajs

Step 3: Creating Microservices

For our example, let’s create two microservices: Users and Products. Each will handle its own data and will communicate via Kafka.

Creating the Users Microservice

  1. Generate a new module and service:
    nest generate module users
    nest generate service users
  2. In the users.service.ts, implement basic user management functionality:
  3. import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class UsersService {
      private users = [];
    
      createUser(user: { name: string; age: number }) {
        this.users.push(user);
        return user;
      }
    
      findAll() {
        return this.users;
      }
    }
  4. Create a controller to handle HTTP requests:
    nest generate controller users
  5. In users.controller.ts, set up endpoints:
  6. import { Controller, Post, Body, Get } from '@nestjs/common';
    import { UsersService } from './users.service';
    
    @Controller('users')
    export class UsersController {
      constructor(private readonly usersService: UsersService) {}
    
      @Post()
      create(@Body() user: { name: string; age: number }) {
        return this.usersService.createUser(user);
      }
    
      @Get()
      findAll() {
        return this.usersService.findAll();
      }
    }
  7. In the main.ts file, set up the microservice:
  8. import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { MicroserviceOptions, Transport } from '@nestjs/microservices';
    
    async function bootstrap() {
      const app = await NestFactory.createMicroservice(AppModule, {
        transport: Transport.KAFKA,
        options: {
          client: {
            clientId: 'users-service',
            brokers: ['localhost:9092'], // Replace with your Kafka broker address
          },
          consumer: {
            groupId: 'users-consumer', // Unique group ID for this consumer
          },
        },
      });
      await app.listen();
    }
    
    bootstrap();

Creating the Products Microservice

Repeat similar steps for the Products microservice:

  1. Generate the module and service:
    nest generate module products
    nest generate service products
  2. Implement the products.service.ts:
    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class ProductsService {
      private products = [];
    
      createProduct(product: { name: string; price: number }) {
        this.products.push(product);
        return product;
      }
    
      findAll() {
        return this.products;
      }
    }
  3. Set up the controller for products:
    nest generate controller products
  4. In products.controller.ts, set up endpoints:
  5. import { Controller, Post, Body, Get } from '@nestjs/common';
    import { ProductsService } from './products.service';
    
    @Controller('products')
    export class ProductsController {
      constructor(private readonly productsService: ProductsService) {}
    
      @Post()
      create(@Body() product: { name: string; price: number }) {
        return this.productsService.createProduct(product);
      }
    
      @Get()
      findAll() {
        return this.productsService.findAll();
      }
    }
  6. Set up the microservice in main.ts:
  7. import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { MicroserviceOptions, Transport } from '@nestjs/microservices';
    
    async function bootstrap() {
      const app = await NestFactory.createMicroservice(AppModule, {
        transport: Transport.KAFKA,
        options: {
          client: {
            clientId: 'products-service',
            brokers: ['localhost:9092'], // Replace with your Kafka broker address
          },
          consumer: {
            groupId: 'products-consumer', // Unique group ID for this consumer
          },
        },
      });
      await app.listen();
    }
    
    bootstrap();

Inter-Service Communication with Kafka

To enable communication between the Users and Products microservices using Kafka, we will set up message patterns.

Step 1: Sending Messages from Users Service to Products Service

In the Users service, we will publish an event to Kafka whenever a user is created. Modify the UsersService to include Kafka publishing:

import { Injectable, Inject } from '@nestjs/common';
import { ClientKafka } from '@nestjs/microservices';

@Injectable()
export class UsersService {
  constructor(@Inject('PRODUCTS_SERVICE') private readonly client: ClientKafka) {}

  async createUser(user: { name: string; age: number }) {
    const createdUser = { name: user.name, age: user.age };
    // Publish user created event
    await this.client.emit('user_created', createdUser);
    return createdUser;
  }

  // Other methods...
}

Step 2: Consuming Messages in Products Service

In the Products service, we will listen for the user_created event and handle it:

import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';

@Controller('products')
export class ProductsController {
  // Other methods...

  @MessagePattern('user_created')
  handleUserCreated(user: { name: string; age: number }) {
    console.log(`User created: ${JSON.stringify(user)}`);
    // You can add logic here to create related products or perform actions
  }
}

Testing the Microservices

Now that our microservices are set up, let’s run them:

  1. Start the Users microservice:
    cd users
    npm run start --watch
  2. In a separate terminal, start the Products microservice:
    cd products
    npm run start --watch
  3. Use a tool like Postman or curl to test the endpoints.

To create a user:

curl -X POST http://localhost:3000/users -H 'Content-Type: application/json' -d '{"name": "John Doe", "age": 30}'

To retrieve all users:

curl -X GET http://localhost:3000/users

To create a product:

curl -X POST http://localhost:3001/products -H 'Content-Type: application/json' -d '{"name": "Product 1", "price": 100}'

To retrieve all products:

curl -X GET http://localhost:3001/products

Patterns

Microservices recognize both messages and events by patterns. A pattern is a plain value, for example, a literal object or a string. Patterns are automatically serialized and sent over the network along with the data portion of a message. In this way, message senders and consumers can coordinate which requests are consumed by which handlers.

Request-Response

The request-response message style is useful when you need to exchange messages between various external services. With this paradigm, you can be certain that the service has actually received the message (without the need to manually implement a message ACK protocol). However, the request-response paradigm is not always the best choice. For example, streaming transporters that use log-based persistence, such as Kafka or NATS streaming, are optimized for solving a different range of issues, more aligned with an event messaging paradigm.

Conclusion

In this blog post, we’ve explored the fundamentals of creating microservices using NestJS. We developed two microservices, Users and Products, that communicate via Kafka. This example demonstrates how easy it is to build scalable and maintainable applications using NestJS.

With NestJS, you can build complex microservices architectures that are easy to manage and scale. The framework’s powerful features and ease of use make it an excellent choice for modern web applications.

Feel free to experiment with this example, add more features, and explore the various transport layers NestJS offers for microservices!

LET'S BUILD WHAT'S NEXT

Ready to turn your idea into a real solution?

Let's discuss how we can help you create value with technology.

Let's Talk