DEV Community

Vishnu C Prasad
Vishnu C Prasad

Posted on • Originally published at vishnucprasad.Medium

Implementing the CQRS Pattern in NestJS with a Note API Example

CQRS Pattern in NestJS

In the world of software architecture, designing systems that are scalable, maintainable, and efficient is a paramount concern. One architectural pattern that has gained traction for achieving these goals is the Command Query Responsibility Segregation (CQRS) pattern. CQRS is particularly useful for applications that require complex data manipulation and querying. In this article, we’ll explore how to implement the CQRS pattern in a NestJS application by creating a Note API example.

Understanding the CQRS Pattern

CQRS is an architectural pattern that separates the responsibilities of handling read and write operations in a system. It recognizes that the requirements for reading data are often different from those for writing data. By segregating these responsibilities, CQRS allows for optimizing the read and write paths independently, leading to improved performance, scalability, and maintainability.

In a CQRS-based architecture, there are two main components: the Command side and the Query side.

  • Command Side: This side is responsible for handling write operations such as creating, updating, and deleting data. It uses commands to trigger changes in the application’s state.
  • Query Side: This side is responsible for handling read operations. It focuses on delivering data in a format that is optimized for querying and displaying, without concerns about the underlying data storage or manipulation logic.

Implementing CQRS in NestJS

NestJS is a popular Node.js framework that provides a solid foundation for building scalable and maintainable server-side applications. To implement the CQRS pattern in NestJS, we’ll use the @nestjs/cqrs package, which provides tools and decorators for managing commands, events, and queries.

Let’s walk through the implementation of a Note API using CQRS in NestJS.

Step 1: Setting Up the Project

First, create a new NestJS project if you haven’t already:

nest new note-api-cqrs
Enter fullscreen mode Exit fullscreen mode

Navigate to the project directory:

cd note-api-cqrs
Enter fullscreen mode Exit fullscreen mode

Step 2: Installing Dependencies

Install the required dependencies for CQRS and other functionalities:

npm install --save @nestjs/cqrs
Enter fullscreen mode Exit fullscreen mode

Step 3: Creating Commands, Events, and Queries

In CQRS, commands represent actions that modify the application’s state, events represent things that have happened in the past, and queries represent requests for information. Let’s create these components for our Note API.

Create a notes module:

nest generate module notes
Enter fullscreen mode Exit fullscreen mode

Step 4: Creating Command

Inside the notes module, create a create-note.command.ts file and implement the create note command.

export class CreateNoteCommand {
  constructor(public readonly title: string, public readonly content: string) {}
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Creating Query

Inside the notes module, create a get-notes.query.ts file and implement the get notes query.

export class GetNotesQuery {}
Enter fullscreen mode Exit fullscreen mode

Step 6: Implementing Command Handlers

Create a create-note.handler.ts file and implement the command handler:

import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { CreateNoteCommand } from './create-note.command';
import { NoteCreatedEvent } from '../events/note-created.event';

@CommandHandler(CreateNoteCommand)
export class CreateNoteHandler implements ICommandHandler<CreateNoteCommand> {
  async execute(command: CreateNoteCommand): Promise<void> {
    // Here, you can perform the necessary logic to create a new note
    const { title, content } = command;

    // Simulate note creation
    const noteId = Math.random().toString(36).substring(7);

    // Emit a NoteCreatedEvent
    const event = new NoteCreatedEvent(noteId, title, content);
    event.commit();
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Implementing Query Handlers

Create aget-notes.handler.ts file and implement the query handler:

import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { GetNotesQuery } from './get-notes.query';

@QueryHandler(GetNotesQuery)
export class GetNotesHandler implements IQueryHandler<GetNotesQuery> {
  async execute(query: GetNotesQuery): Promise<any> {
    // Here, you can retrieve notes data for querying
    // For demonstration purposes, let's return a mock list of notes
    return [
      { id: 1, title: 'Note 1', content: 'Content of note 1' },
      { id: 2, title: 'Note 2', content: 'Content of note 2' },
    ];
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 8: Wiring Everything Together

In the notes.module.ts file, configure the CQRS module to use the command handlers and query handlers:

import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { NotesController } from './notes.controller';
import { CreateNoteHandler } from './commands/create-note.command';
import { NoteCreatedHandler } from './events/note-created.event';
import { GetNotesHandler } from './queries/get-notes.query';

@Module({
  imports: [CqrsModule],
  controllers: [NotesController],
  providers: [CreateNoteHandler, GetNotesHandler],
})
export class NotesModule {}
Enter fullscreen mode Exit fullscreen mode

Step 9: Creating the Notes Controller

In the notes.controller.ts file, create a controller to handle the API requests:

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { CreateNoteCommand } from './commands/create-note.command';
import { GetNotesQuery } from './queries/get-notes.query';

@Controller('notes')
export class NotesController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
  ) {}

  @Post()
  async createNote(@Body() body: { title: string, content: string }): Promise<void> {
    const { title, content } = body;
    await this.commandBus.execute(new CreateNoteCommand(title, content));
  }

  @Get()
  async getNotes(): Promise<any[]> {
    return this.queryBus.execute(new GetNotesQuery());
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 10: Running the Application

Finally, start the NestJS application:

npm run start:dev
Enter fullscreen mode Exit fullscreen mode

Your Note API with the CQRS pattern is now up and running! You can use tools like curl, Postman, or other API testing tools to interact with the API.

Conclusion

The CQRS pattern is a powerful way to achieve separation of concerns in applications by segregating read and write operations. With the help of the @nestjs/cqrs package, implementing the CQRS pattern in a NestJS application becomes straightforward. By using commands and queries, you can efficiently handle complex data manipulation and querying scenarios while maintaining scalability and maintainability.

Here’s a link to a sample project repository for a more concrete demonstration of the concepts discussed in this article: Github Repo

Top comments (1)

Collapse
 
yannickfricke profile image
Yannick Fricke

The source code for the "NoteCreatedEvent" is missing - but otherwise good job!