Configuring
MBC CQRS Serverless framework allows you to customize your project to meet specific requirements. This guide covers all configuration options available in the framework.
Project Configuration
serverless.yml
The main configuration file for your serverless application.
The framework generates DynamoDB table names automatically using NODE_ENV and APP_NAME environment variables plus a type suffix. The format is {NODE_ENV}-{APP_NAME}-{tableName}-command for command tables and {NODE_ENV}-{APP_NAME}-{tableName}-data for data tables. For example, with NODE_ENV=dev, APP_NAME=my-app, and tableName: 'order', the tables are dev-my-app-order-command and dev-my-app-order-data.
service: my-app
frameworkVersion: '3'
plugins:
- serverless-offline
- serverless-dynamodb
provider:
name: aws
runtime: nodejs20.x
stage: ${opt:stage, 'dev'}
region: ${opt:region, 'ap-northeast-1'}
memorySize: 512
timeout: 30
environment:
NODE_ENV: ${self:provider.stage}
APP_NAME: ${self:service}
COGNITO_USER_POOL_ID: ${env:COGNITO_USER_POOL_ID}
COGNITO_USER_POOL_CLIENT_ID: ${env:COGNITO_USER_POOL_CLIENT_ID}
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.stage}-${self:service}-*"
custom:
serverless-offline:
httpPort: 3000
lambdaPort: 3002
serverless-dynamodb:
stages:
- dev
start:
port: 8000
inMemory: true
migrate: false
NestJS Configuration
Configure NestJS modules in your application.
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CommandModule } from '@mbc-cqrs-serverless/core';
@Module({
imports: [
// Load environment variables
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env.local', '.env'],
}),
// Configure Command Module
CommandModule.register({
tableName: 'order', // Logical name; actual table: {NODE_ENV}-{APP_NAME}-order-command
dataSyncHandlers: [],
}),
],
})
export class AppModule {}
TypeScript Configuration
tsconfig.json
Recommended TypeScript configuration for MBC CQRS Serverless projects.
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2021",
"lib": ["ES2021"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": false,
"inlineSourceMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@shared/*": ["src/shared/*"]
},
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
The paths option enables module path aliases (e.g. @/ for src/). See Absolute Imports and Module Path Aliases for details.
ESLint Configuration
.eslintrc.js
Recommended ESLint configuration.
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js', 'dist', 'node_modules'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
},
};
Module Configuration
CommandModule Options
Configure the core CommandModule.
import { Module } from '@nestjs/common';
import { CommandModule } from '@mbc-cqrs-serverless/core';
import { OrderRdsSyncHandler } from './order-rds-sync.handler'; // Your IDataSyncHandler implementation — see service-patterns
@Module({
imports: [
CommandModule.register({
// Required: DynamoDB table name
tableName: 'order',
// Optional: Data sync handlers for RDS synchronization
dataSyncHandlers: [OrderRdsSyncHandler],
// Optional: Reserved for future use (not yet implemented)
skipError: false,
// Optional: Disable default DynamoDB data sync handler
disableDefaultHandler: false,
}),
],
})
export class OrderModule {}
SequencesModule Options
Configure auto-incrementing sequences.
import { Module } from '@nestjs/common';
import { SequencesModule } from '@mbc-cqrs-serverless/sequence';
@Module({
imports: [
SequencesModule.register({
// Optional: Enable or disable default sequence controller
enableController: true,
}),
],
})
export class AppModule {}
Note: Rotation strategy (day, month, year, fiscal_yearly, none) is configured through master data settings, not module options. See the Sequence documentation for details.
TenantModule Options
Configure multi-tenant support.
import { Module } from '@nestjs/common';
import { TenantModule } from '@mbc-cqrs-serverless/tenant';
import { TenantRdsSyncHandler } from './tenant-rds-sync.handler'; // Your IDataSyncHandler implementation — see service-patterns
@Module({
imports: [
TenantModule.register({
// Optional: Enable or disable default tenant controller
enableController: true,
// Optional: Data sync handlers for RDS synchronization
dataSyncHandlers: [TenantRdsSyncHandler],
}),
],
})
export class AppModule {}
NotificationModule
Configure email and real-time notifications via environment variables.
import { Module } from '@nestjs/common';
import { NotificationModule } from '@mbc-cqrs-serverless/core';
@Module({
imports: [
// NotificationModule is a global module - just import it
NotificationModule,
],
})
export class AppModule {}
NotificationModule is configured through environment variables:
# Email (SES) configuration
# Required: Default sender email address
SES_FROM_EMAIL=noreply@example.com
# Optional: AWS SES endpoint (for local development)
SES_ENDPOINT=http://localhost:4566
# Optional: AWS SES region
SES_REGION=ap-northeast-1
# Real-time transport configuration
# Optional: Active transports — default is appsync-graphql (GraphQL Subscriptions)
# Set to appsync-event for AppSync Events API (v1.3.0+), or comma-separated for dual-publish
NOTIFICATION_TRANSPORTS=appsync-graphql
# Required when NOTIFICATION_TRANSPORTS includes appsync-graphql
APPSYNC_ENDPOINT=https://xxxx.appsync-api.ap-northeast-1.amazonaws.com/graphql
APPSYNC_API_KEY=da2-xxxx
# Required when NOTIFICATION_TRANSPORTS includes appsync-event (v1.3.0+)
# APPSYNC_EVENTS_ENDPOINT=https://xxxx.appsync-api.ap-northeast-1.amazonaws.com/event
See Notification Module for full AppSync transport setup and real-time subscription patterns.
Logging Configuration
Configure Logger
Set up structured logging for production.
import { Injectable, Logger } from '@nestjs/common';
// Configure log level based on environment
const LOG_LEVELS = {
production: ['error', 'warn', 'log'],
development: ['error', 'warn', 'log', 'debug', 'verbose'],
test: ['error', 'warn'],
};
// In main.ts or bootstrap
const app = await NestFactory.create(AppModule, {
logger: LOG_LEVELS[process.env.NODE_ENV] || LOG_LEVELS.development,
});
// Structured logging in services
@Injectable()
export class OrderService {
private readonly logger = new Logger(OrderService.name);
async createOrder(dto: CreateOrderDto): Promise<Order> {
this.logger.log({
message: 'Creating order',
code: dto.code,
tenantCode: dto.tenantCode,
});
// ... implementation
}
}
Validation Configuration
Global Validation Pipe
Configure validation for all endpoints.
import { BadRequestException, ValidationPipe } from '@nestjs/common';
// In main.ts or bootstrap
app.useGlobalPipes(
new ValidationPipe({
// Transform payloads to DTO instances
transform: true,
// Strip properties not in DTO
whitelist: true,
// Throw error for extra properties
forbidNonWhitelisted: true,
// Transform primitive types
transformOptions: {
enableImplicitConversion: true,
},
// Custom error messages
exceptionFactory: (errors) => {
const messages = errors.map(error => ({
field: error.property,
errors: Object.values(error.constraints || {}),
}));
return new BadRequestException({
message: 'Validation failed',
errors: messages,
});
},
}),
);
CORS Configuration
Configure CORS
Set up Cross-Origin Resource Sharing.
// In main.ts
app.enableCors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || [
'http://localhost:3000',
],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-Id'],
credentials: true,
maxAge: 3600,
});
Stage-Specific Configuration
Environment-Based Settings
Configure different settings per environment.
// config/configuration.ts
export default () => ({
stage: process.env.STAGE || 'dev',
database: {
tablePrefix: `myapp-${process.env.STAGE}`,
commandTable: `myapp-${process.env.STAGE}-command`,
dataTable: `myapp-${process.env.STAGE}-data`,
},
aws: {
region: process.env.AWS_REGION || 'ap-northeast-1',
dynamodbEndpoint: process.env.DYNAMODB_ENDPOINT, // For local dev
},
auth: {
userPoolId: process.env.COGNITO_USER_POOL_ID,
clientId: process.env.COGNITO_USER_POOL_CLIENT_ID,
},
features: {
enableDebugLogs: process.env.STAGE === 'dev',
enableMetrics: process.env.STAGE === 'prod',
},
});
// Usage
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppService {
constructor(private configService: ConfigService) {}
getTableName(): string {
return this.configService.get<string>('database.commandTable');
}
}
Related Documentation
- Environment Variables - All available environment variables
- Modules - Module configuration and registration
- Command Service - CommandModule behavior and options
- Sequence - SequencesModule for auto-incrementing IDs
- Tenant Module - TenantModule configuration
- Notification Module - Email and real-time notification configuration
- Email Service - SES-based email with templates
- Deployment Guide - Configure for production