Nest

How do we use CQRS with @nestjs/cqrs to solve scalability issues in high-traffic apps?

March 18, 2026

download ready
Thank You
Your submission has been received.
We will be in touch and contact you soon!

CQRS splits your app into separate "Command" paths (for writes/changes) and "Query" paths (for reads), letting you scale reads and writes independently—perfect for high-traffic apps where one grows faster than the other.​

Install @nestjs/cqrs, create Commands for mutations (create/update/delete) handled by @CommandHandlers, Queries for reads handled by @QueryHandlers, and use different databases or scaling strategies for each path. This decouples read-heavy traffic (caching, replicas) from write-heavy operations (transactions, queues), solving bottlenecks in high-traffic scenarios. Events via EventBus keep everything in sync across services.

  • Install: npm i @nestjs/cqrs
  • app.module.ts

Code

import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
@Module({
  imports: [CqrsModule.forRoot()],
  // your controllers, handlers...
})
export class AppModule {}
      

Step 1:-
Command (write operation)

Code

export class CreateUserCommand {
  constructor(
    public readonly name: string,
    public readonly email: string,
  ) {}
}
      

Step 2:-
Command Handler

Code

import { ICommandHandler, CommandHandler } from '@nestjs/cqrs';
@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
  async execute(command: CreateUserCommand) {
    // Write to main DB/transactional store
    const user = await this.userRepo.create(command);
    return user.id; // Return result
  }
}
      

Step 3:-
Query (read operation)

Code

 export class GetUserQuery {
  constructor(public readonly id: string) {}
}
      

Step 4:-
Query Handler

Code

import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
@QueryHandler(GetUserQuery)
export class GetUserHandler implements IQueryHandler<GetUserQuery> {
  async execute(query: GetUserQuery) {
    // Read from cache/read replica
    return this.userReadService.findById(query.id);
  }
}
      

Step 5:-
Controller usage

Code

import { CommandBus, QueryBus } from '@nestjs/cqrs';

@Controller('users')
export class UserController {
  constructor(
    private commandBus: CommandBus,
    private queryBus: QueryBus,
  ) {}

  @Post()
  async create(@Body() dto: CreateUserDto) {
    return this.commandBus.execute(new CreateUserCommand(dto.name, dto.email));
  }

  @Get(':id')
  async find(@Param('id') id: string) {
    return this.queryBus.execute(new GetUserQuery(id));
  }
}
      
Hire Now!

Need Help with Nest Development ?

Work with our skilled nest developers to accelerate your project and boost its performance.
**Hire now**Hire Now**Hire Now**Hire now**Hire now

How do we use CQRS with @nestjs/cqrs to solve scalability issues in high-traffic apps?

CQRS splits your app into separate "Command" paths (for writes/changes) and "Query" paths (for reads), letting you scale reads and writes independently—perfect for high-traffic apps where one grows faster than the other.​

Install @nestjs/cqrs, create Commands for mutations (create/update/delete) handled by @CommandHandlers, Queries for reads handled by @QueryHandlers, and use different databases or scaling strategies for each path. This decouples read-heavy traffic (caching, replicas) from write-heavy operations (transactions, queues), solving bottlenecks in high-traffic scenarios. Events via EventBus keep everything in sync across services.

  • Install: npm i @nestjs/cqrs
  • app.module.ts

Code

import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
@Module({
  imports: [CqrsModule.forRoot()],
  // your controllers, handlers...
})
export class AppModule {}
      

Step 1:-
Command (write operation)

Code

export class CreateUserCommand {
  constructor(
    public readonly name: string,
    public readonly email: string,
  ) {}
}
      

Step 2:-
Command Handler

Code

import { ICommandHandler, CommandHandler } from '@nestjs/cqrs';
@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
  async execute(command: CreateUserCommand) {
    // Write to main DB/transactional store
    const user = await this.userRepo.create(command);
    return user.id; // Return result
  }
}
      

Step 3:-
Query (read operation)

Code

 export class GetUserQuery {
  constructor(public readonly id: string) {}
}
      

Step 4:-
Query Handler

Code

import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
@QueryHandler(GetUserQuery)
export class GetUserHandler implements IQueryHandler<GetUserQuery> {
  async execute(query: GetUserQuery) {
    // Read from cache/read replica
    return this.userReadService.findById(query.id);
  }
}
      

Step 5:-
Controller usage

Code

import { CommandBus, QueryBus } from '@nestjs/cqrs';

@Controller('users')
export class UserController {
  constructor(
    private commandBus: CommandBus,
    private queryBus: QueryBus,
  ) {}

  @Post()
  async create(@Body() dto: CreateUserDto) {
    return this.commandBus.execute(new CreateUserCommand(dto.name, dto.email));
  }

  @Get(':id')
  async find(@Param('id') id: string) {
    return this.queryBus.execute(new GetUserQuery(id));
  }
}