Skip to content

Microservice Setup Guide

This system follows a microservices architecture with Bounded Context (BC) patterns. Each microservice:

  • Owns its domain data (Single Source of Truth)
  • Exposes both HTTP REST APIs and TCP microservice endpoints
  • Communicates with other services via TCP transport
  • Uses standardized bootstrap configuration
  • Integrates with Redis (stateful JWT sessions), PostgreSQL (primary-replica), and object storage

Use the NestJS CLI to generate a new application in the monorepo:

Terminal window
# For Bounded Context services (business domains)
nest g app <business>-bc
# Examples:
nest g app data-owner-bc # Data Owner Bounded Context
nest g app data-consumer-bc # Data Consumer Bounded Context
nest g app reporting-bc # Reporting BC
# For non-BC services (infrastructure/support services)
nest g app <service-name>
# Examples:
nest g app auth # Authentication service
nest g app iam # Identity & Access Management
nest g app storage # File storage service

Naming Convention:

  • Use kebab-case for application names
  • Suffix business domain services with -bc (Bounded Context)
  • Infrastructure services (auth, storage, etc.) don’t use -bc suffix

What this creates:

apps/<service-name>/
├── src/
│ ├── <service-name>.controller.ts
│ ├── <service-name>.module.ts
│ ├── <service-name>.service.ts
│ └── main.ts
├── test/
│ └── app.e2e-spec.ts
├── tsconfig.app.json
└── package.json (if standalone)

Replace the default main.ts with a standardized bootstrap using the bootstrapApplication() utility.

2.1 Import Required Dependencies

Create apps/<service-name>/src/main.ts:

import { DocAuthKey } from '@lib/common/enum/auth/doc-auth-key.enum';
import { bootstrapApplication } from '@lib/common/utils/bootstrap.util';
import { YourBCModule } from './your-bc.module';
async function bootstrap(): Promise<void> {
await bootstrapApplication({
// Configuration options...
});
}
bootstrap();

2.2 Configure Bootstrap Options

The bootstrapApplication() function accepts a configuration object with the following parameters:

await bootstrapApplication({
// Module Configuration
module: YourBCModule, // Your root module class
// Environment Variable Keys (for ConfigService)
globalPrefixNameEnv: 'YOUR_PREFIX_NAME', // e.g., 'DATA_OWNER_PREFIX_NAME'
globalPrefixVersionEnv: 'YOUR_PREFIX_VERSION', // e.g., 'DATA_OWNER_PREFIX_VERSION'
httpPortEnv: 'YOUR_BC_MODULE_HTTP_PORT', // e.g., 'DATA_OWNER_BC_HTTP_PORT'
microservicePortEnv: 'YOUR_BC_MODULE_MICROSERVICE_PORT', // Optional for microservices
// Default Values (fallbacks if env vars not set)
defaultGlobalPrefixName: 'your-bc', // URL prefix: /your-bc/v1
defaultGlobalPrefixVersion: 'v1', // API version
// Swagger/OpenAPI Documentation
swagger: {
title: 'Your BC API',
description: 'The API documentation for Your Bounded Context.',
version: '1.0', // Optional, defaults to '1.0'
tag: 'Your BC Service',
},
// JWT Authentication (for protected APIs)
jwtAuth: {
name: DocAuthKey.JwtName, // Must match @ApiBearerAuth() decorator
description: 'Enter your bearer token to authenticate',
},
});

2.3 Complete Example (Data Owner BC)

import { DocAuthKey } from '@lib/common/enum/auth/doc-auth-key.enum';
import { bootstrapApplication } from '@lib/common/utils/bootstrap.util';
import { DataOwnerBCModule } from './data-owner-bc.module';
async function bootstrap(): Promise<void> {
await bootstrapApplication({
module: DataOwnerBCModule,
globalPrefixNameEnv: 'DATA_OWNER_PREFIX_NAME',
globalPrefixVersionEnv: 'DATA_OWNER_PREFIX_VERSION',
defaultGlobalPrefixName: 'data-owner-bc',
defaultGlobalPrefixVersion: 'v1',
httpPortEnv: 'DATA_OWNER_BC_MODULE_HTTP_PORT',
microservicePortEnv: 'DATA_OWNER_BC_MODULE_MICROSERVICE_PORT',
swagger: {
title: 'Data Owner API',
description: 'The API documentation for the Data Owner Bounded Context.',
tag: 'Data Owner BC',
},
jwtAuth: {
name: DocAuthKey.JwtName,
description: 'Enter your bearer token to authenticate',
},
});
}
bootstrap();

What Bootstrap Automatically Configures:

  • CORS enabled
  • Global prefix: /{prefix}/{version} (e.g., /data-owner-bc/v1)
  • Global exception filter (AllExceptionsFilter)
  • Response transformation interceptor (TransformInterceptor)
  • Authentication guards (AuthGuard, PermissionsGuard)
  • Validation pipe with DTO validation
  • Swagger/OpenAPI documentation
  • TCP microservice listener (if microservicePortEnv provided)
  • Health check endpoint at /health

Step 3: Add Environment Variables to ConfigModule

Section titled “Step 3: Add Environment Variables to ConfigModule”

The ConfigModule uses Joi for validation. Add your service’s environment variables.

3.1 Open Configuration File

File: libs/config/src/config.module.ts

3.2 Add Validation Schema

Add your service’s variables to the Joi validation schema:

validationSchema: Joi.object({
// ... existing variables ...
// Your New Service
YOUR_PREFIX_NAME: Joi.string().required(),
YOUR_PREFIX_VERSION: Joi.string().required(),
YOUR_BC_MODULE_MICROSERVICE_PORT: Joi.number().required(),
YOUR_BC_MODULE_HTTP_PORT: Joi.number().required(),
}),

Example (Reporting BC):

// Reporting Bounded Context (BC)
REPORTING_PREFIX_NAME: Joi.string().required(),
REPORTING_PREFIX_VERSION: Joi.string().required(),
REPORTING_BC_MODULE_MICROSERVICE_PORT: Joi.number().required(),
REPORTING_BC_MODULE_HTTP_PORT: Joi.number().required(),

Validation Rules:

  • Use .required() for mandatory variables
  • Use .default(value) for optional variables with fallbacks
  • Use appropriate Joi types: .string(), .number(), .boolean()

Add your service’s runtime configuration to the .env file.

4.1 Port Allocation Strategy

Port Ranges:

  • Microservice TCP Ports: 3000–3999, 5000–5999
  • HTTP Ports: 4000–4999, 6000–6999

Convention: Assign microservice and HTTP ports sequentially — if the TCP port is 3003, the HTTP port should be 4003.

4.2 Add Configuration

Terminal window
#--- Your Service Name ---#
YOUR_PREFIX_NAME=your-service
YOUR_PREFIX_VERSION=v1
YOUR_BC_MODULE_MICROSERVICE_PORT=3003 # Choose available port
YOUR_BC_MODULE_HTTP_PORT=4003 # HTTP = Microservice + 1000

Example (Reporting BC):

Terminal window
#--- Reporting BC Service ---#
REPORTING_PREFIX_NAME=reporting-bc
REPORTING_PREFIX_VERSION=v1
REPORTING_BC_MODULE_MICROSERVICE_PORT=3003
REPORTING_BC_MODULE_HTTP_PORT=4003

Best Practices:

  • Keep microservice and HTTP ports sequential (e.g., 3003 and 4003)
  • Document port allocation in comments
  • Update .env.example as well for team reference

Add your service to the microservice registry for inter-service communication.

5.1 Open Enum File

File: libs/common/src/enum/app-microservice.enum.ts

5.2 Define Service Constants

// Add your service constant
export const YourServiceMCS = {
name: 'YOUR_SERVICE_NAME',
cmd: {
// Define message patterns for inter-service communication
GetResource: 'yourService.getResource',
CreateResource: 'yourService.createResource',
},
};
// Update AppMicroservice object
export const AppMicroservice = {
Auth: AuthMCS,
Iam: IamMCS,
MasterData: MasterDataMCS,
Storage: StorageMCS,
SystemAdmin: SystemAdminMCS,
YourService: YourServiceMCS, // Add your service here
};

Example (Reporting BC):

export const ReportingMCS = {
name: 'REPORTING_SERVICE',
cmd: {
GetReportById: 'reporting.getReportById',
GenerateReport: 'reporting.generateReport',
GetScheduledReports: 'reporting.getScheduledReports',
},
};
export const AppMicroservice = {
Auth: AuthMCS,
Iam: IamMCS,
MasterData: MasterDataMCS,
Storage: StorageMCS,
SystemAdmin: SystemAdminMCS,
Reporting: ReportingMCS,
};

Naming Conventions:

  • Service name: SCREAMING_SNAKE_CASE
  • Message patterns: camelCase with dot notation (service.action)
  • Pattern format: {serviceName}.{resourceOrAction}.{operation}

Step 6: Register Microservice Client in CommonModule

Section titled “Step 6: Register Microservice Client in CommonModule”

Enable other services to communicate with your new microservice.

6.1 Open CommonModule File

File: libs/common/src/common.module.ts

6.2 Add Client Registration

ClientsModule.registerAsync([
// ... existing services ...
// Your New Service Client
{
name: AppMicroservice.YourService.name,
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
transport: Transport.TCP,
options: {
host: 'localhost',
port: configService.get<number>(
'YOUR_BC_MODULE_MICROSERVICE_PORT',
3003, // Default fallback port
),
},
}),
inject: [ConfigService],
},
]),

Configuration Details:

  • name: Must match the name in AppMicroservice enum
  • transport: Currently using Transport.TCP (can be REDIS, KAFKA, etc.)
  • host: localhost for development, configure for production
  • port: Read from environment with fallback default

Step 7: Register API Documentation in Docs Gateway

Section titled “Step 7: Register API Documentation in Docs Gateway”

Add your service’s Swagger documentation to the centralized docs aggregator.

7.1 Open Docs Gateway Main File

File: apps/docs-gateway/src/main.ts

7.2 Add Service Documentation Source

const scalarOptions: ApiReferenceOptions = {
sources: [
// ... existing services ...
// Your New Service
{
url: `http://localhost:${configService.get<number>('YOUR_BC_MODULE_HTTP_PORT')}/${configService.get<string>('YOUR_PREFIX_NAME')}/${configService.get<string>('YOUR_PREFIX_VERSION')}/${swaggerJsonEndpoint}`,
title: 'Your Service Name',
},
],
};

URL Construction:

  • Format: http://localhost:{HTTP_PORT}/{PREFIX_NAME}/{PREFIX_VERSION}/{JSON_ENDPOINT}
  • Example: http://localhost:4003/reporting-bc/v1/json-docs

Documentation Access:

  • Aggregated Docs: http://localhost:9999/api-docs (all services)
  • Individual Service: http://localhost:{HTTP_PORT}/{prefix}/{version}/api-docs

Step 8: Update NestJS CLI Configuration (Auto-generated)

Section titled “Step 8: Update NestJS CLI Configuration (Auto-generated)”

The nest-cli.json file is automatically updated when you run nest g app <name>. Verify the entry exists:

8.1 Check nest-cli.json

File: nest-cli.json

{
"projects": {
"your-service": {
"type": "application",
"root": "apps/your-service",
"entryFile": "main",
"sourceRoot": "apps/your-service/src",
"compilerOptions": {
"tsConfigPath": "apps/your-service/tsconfig.app.json",
"plugins": ["@nestjs/swagger"]
}
}
}
}

Key Points:

  • entryFile: Must be "main" (not "main.ts")
  • plugins: Include "@nestjs/swagger" for automatic Swagger metadata generation

Add convenience scripts to package.json for running your service.

9.1 Development Scripts

{
"scripts": {
"start:dev:your-service": "nest start your-service --watch",
"build:your-service": "nest build your-service",
"test:your-service": "jest --config ./apps/your-service/test/jest-e2e.json"
}
}

Step 9.2: Add Entity Paths to TypeORM Data Sources

Once your microservice has entities that need to be migrated, register them in the TypeORM data source configuration files.

9.2.1 Update Core Database Data Source

File: libs/database/src/data-source.core.ts

export const coreConfig: DataSourceOptions = {
type: 'postgres',
database: AppDatabases.APP_CORE,
synchronize: false,
logging: true,
entities: [
'apps/data-owner-bc/src/**/*.entity.ts',
'apps/data-consumer-bc/src/**/*.entity.ts',
'apps/admin-bc/src/**/*.entity.ts',
'apps/your-service-bc/src/**/*.entity.ts', // ← ADD THIS LINE
],
migrations: ['libs/database/src/migrations/core/*.ts'],
};

9.2.2 Create Migration Directory Structure

Terminal window
# For APP_CORE database
mkdir -p apps/your-service-bc/migrations/core
# For secondary databases (if applicable)
mkdir -p apps/your-service-bc/migrations/secondary

Key Points:

  • The entity path pattern 'apps/<service>/src/**/*.entity.ts' must match your actual entity file locations
  • Add paths before running migrations
  • TypeORM uses these paths to auto-detect entities for schema generation
  • Each service should have its own migration directory to avoid conflicts

Step 9.3: Add Microservice Configuration to PM2 (Production)

Section titled “Step 9.3: Add Microservice Configuration to PM2 (Production)”

For production deployment with PM2, register your microservice in the ecosystem configuration.

File: ecosystem.config.js (root directory)

{
name: 'your-service-bc',
script: './dist/apps/your-service-bc/main.js',
instances: 1,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
YOUR_PREFIX_NAME: 'your-service',
YOUR_PREFIX_VERSION: 'v1',
YOUR_BC_MODULE_HTTP_PORT: 4003,
YOUR_BC_MODULE_MICROSERVICE_PORT: 3003,
},
},

Key Configuration Details:

  • name: Unique identifier for the process
  • script: Path to compiled main.js file
  • instances: Number of process instances (1 for single instance, 'max' for auto-scaling)
  • exec_mode: 'cluster' for multi-core, 'fork' for single process

PM2 Commands:

Terminal window
pm2 start ecosystem.config.js # Start all services
pm2 list # Monitor running services
pm2 logs your-service-bc # View service logs
pm2 restart your-service-bc # Restart service

Ensure environment variables in ecosystem.config.js match your .env and config.module.ts exactly.


Step 10: Configure API Gateway (Production)

Section titled “Step 10: Configure API Gateway (Production)”

For production deployment, register your service in the API Gateway (Kong or equivalent).

10.1 Add Service:

  • Name: your-service-name
  • Protocol: http
  • Host: localhost (or service hostname)
  • Port: Your HTTP port (e.g., 4003)
  • Path: /{prefix}/{version} (e.g., /reporting-bc/v1)

10.2 Create Route:

  • Paths: ["/reporting-bc"]
  • Methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
  • Strip path: false

10.3 Apply Plugins (as needed):

  • Rate limiting
  • CORS
  • JWT authentication
  • Request/response logging

After completing all steps, verify your setup:

  • Service starts without errors: npm run start:dev:your-service
  • HTTP endpoint accessible: http://localhost:{HTTP_PORT}/{prefix}/{version}
  • Health check works: http://localhost:{HTTP_PORT}/health
  • Swagger docs load: http://localhost:{HTTP_PORT}/{prefix}/{version}/api-docs
  • Microservice listens on TCP port (check console logs)
  • Service appears in docs gateway: http://localhost:9999/api-docs
  • Can inject microservice client in other services
  • Environment variables load correctly (no validation errors)
  • Database connections work (if applicable)
  • Redis session management works (if using auth)
Terminal window
# Run unit tests
npm run test:your-service
# Run e2e tests
npm run test:your-service:e2e

Creating a new microservice involves 10 key steps:

  1. Generate app: nest g app <name>-bc
  2. Create bootstrap configuration in main.ts
  3. Add environment variables to libs/config/src/config.module.ts
  4. Add runtime config to .env
  5. Register service in libs/common/src/enum/app-microservice.enum.ts
  6. Register client in libs/common/src/common.module.ts
  7. Add docs to apps/docs-gateway/src/main.ts
  8. Verify nest-cli.json configuration
  9. Add npm scripts and entity paths
  10. Configure API Gateway (production only)

For inter-service communication patterns, EventsController setup, MicroserviceClientService usage, message pattern naming, and troubleshooting:

Microservice Communication Patterns →