Skip to main content
Practice Problems

What is CQRS pattern and how to implement it in NestJS?

CQRS in NestJS

CQRS (Command Query Responsibility Segregation) separates read operations (queries) from write operations (commands). NestJS provides the @nestjs/cqrs package for implementing this pattern.


Why CQRS?

Without CQRSWith CQRS
One model for read & writeSeparate models
Single databaseCan use different DBs
Service does everythingClear separation of concerns
Hard to scale reads/writes independentlyScale independently

Setup

bash
npm install @nestjs/cqrs
typescript
import { Module } from '@nestjs/common'; import { CqrsModule } from '@nestjs/cqrs'; @Module({ imports: [CqrsModule], // ... }) export class OrdersModule {}

Commands (Write Operations)

Define Command

typescript
export class CreateOrderCommand { constructor( public readonly userId: string, public readonly items: { productId: string; quantity: number }[], public readonly shippingAddress: string, ) {} }

Command Handler

typescript
import { CommandHandler, ICommandHandler, EventBus } from '@nestjs/cqrs'; @CommandHandler(CreateOrderCommand) export class CreateOrderHandler implements ICommandHandler<CreateOrderCommand> { constructor( private readonly orderRepository: OrderRepository, private readonly eventBus: EventBus, ) {} async execute(command: CreateOrderCommand) { const { userId, items, shippingAddress } = command; const order = await this.orderRepository.create({ userId, items, shippingAddress, status: 'pending', }); // Publish domain event this.eventBus.publish(new OrderCreatedEvent(order.id, userId)); return order; } }

Queries (Read Operations)

Define Query

typescript
export class GetOrderByIdQuery { constructor(public readonly orderId: string) {} } export class GetUserOrdersQuery { constructor( public readonly userId: string, public readonly page: number = 1, ) {} }

Query Handler

typescript
import { QueryHandler, IQueryHandler } from '@nestjs/cqrs'; @QueryHandler(GetOrderByIdQuery) export class GetOrderByIdHandler implements IQueryHandler<GetOrderByIdQuery> { constructor(private readonly orderReadRepository: OrderReadRepository) {} async execute(query: GetOrderByIdQuery) { return this.orderReadRepository.findById(query.orderId); } }

Events

Define Event

typescript
export class OrderCreatedEvent { constructor( public readonly orderId: string, public readonly userId: string, ) {} }

Event Handler

typescript
import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; @EventsHandler(OrderCreatedEvent) export class OrderCreatedHandler implements IEventHandler<OrderCreatedEvent> { constructor( private readonly emailService: EmailService, private readonly analyticsService: AnalyticsService, ) {} async handle(event: OrderCreatedEvent) { await this.emailService.sendOrderConfirmation(event.userId, event.orderId); await this.analyticsService.trackOrder(event.orderId); } }

Using in Controller

typescript
import { CommandBus, QueryBus } from '@nestjs/cqrs'; @Controller('orders') export class OrdersController { constructor( private readonly commandBus: CommandBus, private readonly queryBus: QueryBus, ) {} @Post() async create(@Body() dto: CreateOrderDto, @CurrentUser() user: User) { return this.commandBus.execute( new CreateOrderCommand(user.id, dto.items, dto.shippingAddress), ); } @Get(':id') async findOne(@Param('id') id: string) { return this.queryBus.execute(new GetOrderByIdQuery(id)); } }

Register Handlers in Module

typescript
const CommandHandlers = [CreateOrderHandler, CancelOrderHandler]; const QueryHandlers = [GetOrderByIdHandler, GetUserOrdersHandler]; const EventHandlers = [OrderCreatedHandler, OrderCancelledHandler]; @Module({ imports: [CqrsModule], controllers: [OrdersController], providers: [ ...CommandHandlers, ...QueryHandlers, ...EventHandlers, OrderRepository, OrderReadRepository, ], }) export class OrdersModule {}

When to Use CQRS

Use CQRS WhenDon't Use When
Complex business logicSimple CRUD apps
Different read/write modelsSmall projects
Event-driven architecturePrototype/MVP
Need audit trailTeam unfamiliar with pattern
Independent scaling of reads/writesOver-engineering risk

Tip: CQRS adds complexity. Start simple with services. Introduce CQRS only when your read and write patterns diverge significantly, or when you need event sourcing.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems