Step3 Microservice

✅ Step 3: Microservices Communication (HTTP + TCP) — Auth Service Example

Goal

  • Auth Service runs both HTTP + TCP.
  • API Gateway connects as a TCP client.
  • A test route (/) on the gateway calls Auth Service over TCP and returns the result.

⚙️ 1) Auth Service — TCP handler + health route

File: apps/auth-service/src/auth-service.controller.ts

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

@Controller()
export class AuthServiceController {
  @Get('health')
  health() {
    return { ok: true, service: 'auth-service', mode: 'HTTP' };
  }

  @MessagePattern({ cmd: 'get_auth' })
  getAuth(@Payload() data: any) {
    return {
      message: '🔑 Auth Service TCP response',
      receivedData: data ?? null,
      ts: new Date().toISOString(),
    };
  }
}

File: apps/auth-service/src/auth-service.module.ts

import { Module } from '@nestjs/common';
import { AuthServiceController } from './auth-service.controller';
import { AuthServiceService } from './auth-service.service';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // Makes env available everywhere
    }),
  ],
  controllers: [AuthServiceController],
  providers: [AuthServiceService],
})
export class AuthServiceModule {}

File: apps/auth-service/src/main.ts

import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { Logger } from '@nestjs/common';
import * as path from 'node:path';
import { AuthServiceModule } from '../../auth-service/src/auth-service.module';

// Dynamically infer service name from directory name
const serviceName = path.basename(path.dirname(__filename)) || 'service';

async function bootstrap() {
  const ENV_PREFIX = serviceName.toUpperCase().replace(/-/g, '_');
  const httpPort = Number(process.env[`${ENV_PREFIX}_HTTP_PORT`]) || 3000;
  const tcpPort = Number(process.env[`${ENV_PREFIX}_TCP_PORT`]) || 4000;

  console.log(`${ENV_PREFIX}_HTTP_PORT`);

  // Create HTTP app
  const app = await NestFactory.create(AuthServiceModule);

  // Attach TCP microservice
  app.connectMicroservice<MicroserviceOptions>({
    transport: Transport.TCP,
    options: { host: '0.0.0.0', port: tcpPort },
  });

  await app.startAllMicroservices();
  await app.listen(httpPort);

  const logger = new Logger(serviceName);
  logger.log(
    `\n🚀  ${serviceName} ready!\n` +
      `    REST: http://localhost:${httpPort}\n` +
      `    TCP : tcp://localhost:${tcpPort}\n` +
      `    ENV : ${process.env.NODE_ENV}`,
  );
}
bootstrap();

⚙️ 2) API Gateway — register TCP client for Auth Service

File: apps/api-gateway/src/api-gateway.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { ApiGatewayController } from './api-gateway.controller';
import { ApiGatewayService } from './api-gateway.service';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    ClientsModule.registerAsync([
      {
        name: 'AUTH_SERVICE',
        imports: [ConfigModule],
        inject: [ConfigService],
        useFactory: (cfg: ConfigService) => ({
          transport: Transport.TCP,
          options: {
            host: '127.0.0.1',
            port: Number(cfg.get('AUTH_SERVICE_TCP_PORT') || 4502),
          },
        }),
      },
    ]),
  ],
  controllers: [ApiGatewayController],
  providers: [ApiGatewayService],
})
export class ApiGatewayModule {}

⚙️ 3) API Gateway Controller → Call Auth Service

File: apps/api-gateway/src/api-gateway.controller.ts

import { Controller, Get, Inject } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { lastValueFrom, timeout, catchError, throwError } from 'rxjs';

@Controller()
export class ApiGatewayController {
  constructor(
    @Inject('AUTH_SERVICE') private readonly authClient: ClientProxy,
  ) {}

  @Get('/')
  async root() {
    const obs$ = this.authClient
      .send({ cmd: 'get_auth' }, { via: 'gateway', at: new Date().toISOString() })
      .pipe(
        timeout(2000),
        catchError((err) =>
          throwError(() => new Error(`Auth service error: ${err?.message || err}`)),
        ),
      );

    const response = await lastValueFrom(obs$);
    return response;
  }

  @Get('/health')
  health() {
    return { ok: true, service: 'api-gateway', mode: 'HTTP' };
  }
}

⚙️ 4) Run in Correct Order

Start Auth Service

npm run start:dev auth-service

Expected log:

🚀  auth-service ready!
    REST: http://localhost:3502
    TCP : tcp://127.0.0.1:4502
    ENV : development

Start API Gateway

npm run start:dev api-gateway

Expected log:

🚀  api-gateway ready!
    REST: http://localhost:3501

⚙️ 5) Verify Communication

Open in browser: http://localhost:3501/

Expected JSON response:

{
  "message": "🔑 Auth Service TCP response",
  "receivedData": { "via": "gateway", "at": "2025-09-11T..." },
  "ts": "2025-09-11T..."
}

Health checks: http://localhost:3501/health → Gateway health http://localhost:3502/health → Auth Service health

Final Verification Checklist for Step 3 (Auth Service)

  • [x] Auth Service runs HTTP 3502 + TCP 4502
  • [x] API Gateway registers TCP client for Auth Service
  • [x] GET / on Gateway returns TCP response from Auth Service

🎉 Important Suggestion
Repeat the process with at least another service to clear your concept. Please follow this guideline before jumping to the next step.