正在加载,请稍候…

NestJS Microservices: Building Scalable Service Architecture

Learn how to build microservices with NestJS using TCP, Redis, Kafka, and gRPC transports. Covers service communication, message patterns, and deployment strategies.

NestJS Microservices: Building Scalable Services

NestJS provides first-class support for building microservices with multiple transport layers. This guide covers practical patterns for production architectures.

Setting Up a Microservice

// main.ts - Microservice entry point
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.TCP,
      options: {
        host: '0.0.0.0',
        port: 3001,
      },
    },
  );
  await app.listen();
  console.log('Microservice is listening');
}
bootstrap();

Message Patterns

// user.controller.ts
import { Controller } from '@nestjs/common';
import { MessagePattern, EventPattern, Payload } from '@nestjs/microservices';

@Controller()
export class UserController {
  constructor(private readonly userService: UserService) {}

  // Request-Response pattern
  @MessagePattern({ cmd: 'get_user' })
  async getUser(@Payload() data: { id: string }) {
    return this.userService.findById(data.id);
  }

  @MessagePattern({ cmd: 'create_user' })
  async createUser(@Payload() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }

  // Fire-and-forget event pattern
  @EventPattern('user.registered')
  async handleUserRegistered(@Payload() event: UserRegisteredEvent) {
    await this.userService.sendWelcomeEmail(event.userId);
  }
}

Client Proxy

// api-gateway/app.module.ts
@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'USER_SERVICE',
        transport: Transport.TCP,
        options: { host: 'user-service', port: 3001 },
      },
      {
        name: 'ORDER_SERVICE',
        transport: Transport.TCP,
        options: { host: 'order-service', port: 3002 },
      },
    ]),
  ],
})
export class AppModule {}

// api-gateway/user.controller.ts
@Controller('users')
export class UserGatewayController {
  constructor(
    @Inject('USER_SERVICE') private readonly userService: ClientProxy,
  ) {}

  @Get(':id')
  getUser(@Param('id') id: string) {
    return this.userService.send({ cmd: 'get_user' }, { id });
  }

  @Post()
  createUser(@Body() dto: CreateUserDto) {
    return this.userService.send({ cmd: 'create_user' }, dto);
  }

  @Post(':id/deactivate')
  deactivateUser(@Param('id') id: string) {
    // Fire-and-forget
    this.userService.emit('user.deactivated', { userId: id });
    return { status: 'queued' };
  }
}

Kafka Transport

// Producer microservice
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
  AppModule,
  {
    transport: Transport.KAFKA,
    options: {
      client: {
        brokers: ['kafka:9092'],
      },
      consumer: {
        groupId: 'order-consumer',
      },
    },
  },
);

// Consumer
@Controller()
export class OrderController {
  @MessagePattern('order.created')
  async handleOrderCreated(
    @Payload() message: any,
    @Ctx() context: KafkaContext,
  ) {
    const originalMessage = context.getMessage();
    const partition = context.getPartition();
    const topic = context.getTopic();

    console.log(`Received from ${topic} [${partition}]`);
    return this.orderService.process(message.value);
  }
}

Redis Transport for Pub/Sub

// Using Redis as message broker
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
  AppModule,
  {
    transport: Transport.REDIS,
    options: {
      host: 'redis',
      port: 6379,
    },
  },
);

// Client module setup
ClientsModule.register([
  {
    name: 'NOTIFICATION_SERVICE',
    transport: Transport.REDIS,
    options: { host: 'redis', port: 6379 },
  },
]),

Exception Filters for Microservices

@Catch()
export class AllExceptionsFilter implements RpcExceptionFilter {
  catch(exception: any, host: ArgumentsHost): Observable<any> {
    const rpcException = new RpcException({
      statusCode: exception.status || 500,
      message: exception.message || 'Internal server error',
    });
    return throwError(() => rpcException);
  }
}

// Apply globally
app.useGlobalFilters(new AllExceptionsFilter());

// Handle in client
this.userService.send({ cmd: 'get_user' }, { id }).pipe(
  catchError(err => {
    throw new HttpException(err.message, err.statusCode || 500);
  }),
);

Health Checks

@Module({
  imports: [
    TerminusModule,
    HttpModule,
  ],
})
export class HealthModule {}

@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private http: HttpHealthIndicator,
    private readonly redis: MicroserviceHealthIndicator,
  ) {}

  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      () => this.http.pingCheck('user-service', 'http://user-service:3001/health'),
      () => this.redis.pingCheck('redis', {
        transport: Transport.REDIS,
        options: { host: 'redis', port: 6379 },
      }),
    ]);
  }
}

Summary

NestJS microservices provide:

  • Multiple transports: TCP, Redis, Kafka, gRPC, NATS
  • Request-response and event patterns out of the box
  • Type-safe client proxies with ClientProxy
  • Built-in exception handling for distributed scenarios
  • Easy integration with Terminus for health checks