Home Blog Page 103

Using Event Emitters and Async Event Handling in NestJS

0
nestjs fullstack course
nestjs fullstack course

Event-driven architecture is a powerful design pattern that promotes loose coupling, scalability, and better organization of logic. In NestJS, you can leverage the built-in event emitter system to handle events asynchronously within your application.

In this module, we’ll explore how to use @nestjs/event-emitter to emit and handle events, process them asynchronously, and integrate this mechanism cleanly into your NestJS apps.


Table of Contents

  1. What Are Event Emitters in NestJS?
  2. When to Use Event Emitters
  3. Installing @nestjs/event-emitter
  4. Emitting Events
  5. Listening to Events with Listeners
  6. Async Event Handling
  7. Real-World Use Cases
  8. Best Practices
  9. Conclusion

What Are Event Emitters in NestJS?

NestJS offers first-class support for event-driven patterns using the @nestjs/event-emitter package. This allows different parts of your application to communicate indirectly through events, making your architecture more modular and decoupled.

Think of it as the pub-sub pattern:

  • Emitters publish events.
  • Listeners subscribe to and respond to events.

When to Use Event Emitters

Use event emitters when:

  • You want to decouple concerns (e.g., send a welcome email after registration).
  • You want to trigger background tasks asynchronously.
  • You need to notify other parts of your app without circular dependencies.

Installing @nestjs/event-emitter

Install the official event emitter module:

npm install @nestjs/event-emitter

Then import the module in your root or feature module:

// app.module.ts
import { Module } from '@nestjs/common';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { UserModule } from './user/user.module';

@Module({
imports: [
EventEmitterModule.forRoot(), // globally sets up emitter
UserModule,
],
})
export class AppModule {}

Emitting Events

You can inject EventEmitter2 and emit custom events from any service.

// user.service.ts
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';

@Injectable()
export class UserService {
constructor(private eventEmitter: EventEmitter2) {}

async registerUser(data: any) {
// logic to create user
const user = { id: 1, email: data.email };

// Emit event after user is registered
this.eventEmitter.emit('user.registered', user);
}
}

You can emit any object, payload, or metadata you want.


Listening to Events with Listeners

Now you need a listener to react to the emitted event.

// user.listener.ts
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserListener {
@OnEvent('user.registered')
handleUserRegisteredEvent(payload: any) {
console.log(`User registered:`, payload);
// e.g., send welcome email or log activity
}
}

Make sure to register the listener in your module:

// user.module.ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserListener } from './user.listener';

@Module({
providers: [UserService, UserListener],
})
export class UserModule {}

Async Event Handling

Event handlers can be asynchronous by returning a Promise.

@OnEvent('user.registered')
async handleUserRegisteredEvent(payload: any) {
await this.mailService.sendWelcomeEmail(payload.email);
}

You can also define event priorities and set wildcards for broader patterns.

Example with wildcard:

@OnEvent('user.*')
handleAllUserEvents(payload: any) {
console.log('User event occurred:', payload);
}

Real-World Use Cases

  • User registration → Send welcome emails.
  • Order placement → Send confirmation and trigger invoice generation.
  • Password reset → Log the attempt and notify via email.
  • Notifications → Trigger real-time socket events based on app activity.

Best Practices

  • Use strongly typed event payloads for better maintainability.
  • Don’t perform heavy tasks directly in listeners—offload to background jobs if needed.
  • Avoid overusing emitters for operations that should be handled via service method calls (keep them purposeful).
  • Structure listeners in separate files/modules for clarity.

Conclusion

Event Emitters in NestJS provide a powerful abstraction for decoupling your application logic and implementing reactive flows. By emitting and listening to events, you make your application more modular, maintainable, and ready for scalability.

Redis Pub/Sub with WebSockets for Scalable Messaging in NestJS

0
nestjs fullstack course
nestjs fullstack course

As your real-time application grows, a single WebSocket server may not be able to handle all clients efficiently. In a distributed system where multiple instances of your NestJS app are running, messages need to be shared across them seamlessly. This is where Redis Pub/Sub comes into play.

In this module, you’ll learn how to integrate Redis with NestJS WebSockets to enable scalable real-time communication using the publish/subscribe pattern.


Table of Contents

  1. Why Use Redis Pub/Sub with WebSockets?
  2. How Pub/Sub Works in Redis
  3. Installing Required Packages
  4. Setting Up Redis Pub/Sub in NestJS
  5. Integrating with WebSocket Gateway
  6. Broadcasting Across Instances
  7. Testing the Scalable Architecture
  8. Security and Best Practices
  9. Conclusion

Why Use Redis Pub/Sub with WebSockets?

When your application scales horizontally (multiple Node.js instances), Socket.IO alone can’t keep all clients in sync. For example, if user A is connected to Instance 1 and user B is on Instance 2, messages between them won’t be delivered unless the instances can share messages.

Redis Pub/Sub acts as a centralized broker to:

  • Publish messages from any instance
  • Distribute messages to all subscribing instances
  • Maintain real-time consistency across distributed systems

How Pub/Sub Works in Redis

The Publisher sends a message to a Redis channel.
The Subscribers (your app instances) listen to that channel and receive the message.

It’s a perfect pattern for broadcasting WebSocket messages across clusters or Docker containers.


Installing Required Packages

You’ll need the following:

npm install redis socket.io-redis
npm install --save-dev @types/redis

For NestJS with Socket.IO adapter:

npm install @nestjs/platform-socket.io @nestjs/websockets

Setting Up Redis Pub/Sub in NestJS

Use socket.io-redis to create a custom adapter.

Step 1: Create a Redis Adapter

// redis.adapter.ts
import { IoAdapter } from '@nestjs/platform-socket.io';
import { ServerOptions } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';

export class RedisIoAdapter extends IoAdapter {
async createIOServer(port: number, options?: ServerOptions): Promise<any> {
const server = super.createIOServer(port, options);

const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();

await pubClient.connect();
await subClient.connect();

server.adapter(createAdapter(pubClient, subClient));

return server;
}
}

Step 2: Apply the Adapter in main.ts

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { RedisIoAdapter } from './redis.adapter';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new RedisIoAdapter(app));
await app.listen(3000);
}
bootstrap();

Integrating with WebSocket Gateway

Your WebSocket gateway doesn’t change much. Redis automatically handles message routing between app instances.

@WebSocketGateway({ cors: true })
export class ChatGateway {
@WebSocketServer()
server: Server;

@SubscribeMessage('sendMessage')
handleMessage(
@MessageBody() payload: { room: string; message: string },
@ConnectedSocket() client: Socket,
) {
this.server.to(payload.room).emit('receiveMessage', {
user: client.id,
message: payload.message,
});
}
}

Broadcasting Across Instances

When a client sends a message, it’s published to Redis. Redis then forwards it to all other connected clients, regardless of the instance they’re on.

This enables:

  • Load balancing WebSocket traffic
  • Real-time chat apps with rooms
  • Microservices communication via WebSockets

Testing the Scalable Architecture

You can simulate this setup by:

  1. Running multiple instances of your NestJS app (e.g., on different ports)
  2. Connecting one client to each instance
  3. Sending a message from one client and verifying the other receives it

You’ll observe that Redis ensures real-time delivery even across app instances.


Security and Best Practices

  • Namespace Isolation: Use namespaces for multi-feature apps (/chat, /notifications, etc.).
  • Authentication: Validate JWT or sessions during connection using middleware or guards.
  • Error Handling: Gracefully manage Redis disconnections or failures.
  • Scaling: Redis handles a lot, but monitor performance under load.

Conclusion

Integrating Redis Pub/Sub with NestJS WebSockets provides a robust foundation for scalable real-time systems. Whether you’re building chat apps, collaboration tools, or live dashboards, Redis ensures consistency and performance in distributed environments.

Building a Chat App with Gateway, Rooms, and Events in NestJS

0
nestjs fullstack course
nestjs fullstack course

Real-time chat applications are a perfect use case for WebSockets. In this module, we’ll build a basic chat app using NestJS WebSocket Gateways, Socket.IO rooms, and custom events. You’ll learn how to structure a real-time communication system with NestJS that supports multiple users and chat rooms.


Table of Contents

  1. Overview of Real-Time Chat Features
  2. Setting Up the WebSocket Gateway
  3. Creating Chat Rooms Using Socket.IO
  4. Managing Users and Broadcasting Events
  5. Frontend Socket.IO Client
  6. Handling Room Events and Messages
  7. Bonus: Persisting Messages (Optional)
  8. Conclusion

Overview of Real-Time Chat Features

In our chat app, users can:

  • Connect via WebSocket
  • Join specific rooms (like channels)
  • Send messages to others in the same room
  • Receive messages in real-time
  • Optionally persist messages in a database

We’ll use:

  • @nestjs/websockets for gateway setup
  • socket.io for client-server real-time communication
  • Simple in-memory logic for rooms and broadcasting

Setting Up the WebSocket Gateway

Install the required packages if you haven’t already:

npm install @nestjs/websockets @nestjs/platform-socket.io socket.io

Create a WebSocket Gateway:

// chat.gateway.ts
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
MessageBody,
ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({ cors: true })
export class ChatGateway {
@WebSocketServer()
server: Server;

@SubscribeMessage('joinRoom')
handleJoinRoom(
@MessageBody() room: string,
@ConnectedSocket() client: Socket,
) {
client.join(room);
client.emit('joinedRoom', `Joined room: ${room}`);
}

@SubscribeMessage('sendMessage')
handleMessage(
@MessageBody() payload: { room: string; message: string },
@ConnectedSocket() client: Socket,
) {
const { room, message } = payload;
this.server.to(room).emit('receiveMessage', {
user: client.id,
message,
});
}
}

Creating Chat Rooms Using Socket.IO

With Socket.IO, creating rooms is simple. You use client.join(roomName) to add a client to a room. Rooms can be dynamic and identified by chat group names, user IDs, or custom IDs.

client.join('dev-room'); // Adds client to the 'dev-room'

Messages can then be scoped to a room:

this.server.to('dev-room').emit('receiveMessage', payload);

Managing Users and Broadcasting Events

To enhance functionality, you can track connected users and rooms using a simple map or a database.

Example:

const activeUsers: Record<string, string> = {}; // clientId -> username

@SubscribeMessage('register')
handleRegister(
@MessageBody() username: string,
@ConnectedSocket() client: Socket,
) {
activeUsers[client.id] = username;
client.emit('registered', `Welcome, ${username}`);
}

When a user sends a message:

this.server.to(room).emit('receiveMessage', {
user: activeUsers[client.id] || client.id,
message,
});

Frontend Socket.IO Client

<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
<script>
const socket = io('http://localhost:3000');

socket.emit('joinRoom', 'room1');

socket.on('joinedRoom', (msg) => {
console.log(msg);
});

socket.emit('sendMessage', {
room: 'room1',
message: 'Hello Room!',
});

socket.on('receiveMessage', (msg) => {
console.log(`[${msg.user}]: ${msg.message}`);
});
</script>

Handling Room Events and Messages

You can extend your gateway to handle:

  • User typing indicators
  • Message read receipts
  • User disconnection announcements
  • Notifications for new users

Example: Notify room on user join

handleJoinRoom(room: string, client: Socket) {
client.join(room);
this.server.to(room).emit('userJoined', {
user: client.id,
room,
});
}

Bonus: Persisting Messages (Optional)

For production apps, persist messages using a database:

  1. Create a Message entity/model
  2. Use a MessageService to save chat data
  3. Call the service in handleMessage
await this.messageService.save({
room,
userId: client.id,
message,
timestamp: new Date(),
});

Conclusion

You’ve now built a simple but powerful chat app using NestJS Gateways and Socket.IO. You’ve learned how to:

  • Create and manage chat rooms
  • Handle real-time message delivery
  • Build a frontend that interacts with the backend
  • Optionally persist chat messages

WebSockets in NestJS with @nestjs/websockets

0
nestjs fullstack course
nestjs fullstack course

In the modern web, real-time communication is increasingly becoming a necessity. Whether it’s a live chat app, real-time analytics dashboard, multiplayer game, or stock ticker, WebSockets enable persistent two-way communication between client and server.

NestJS provides first-class support for WebSockets via the @nestjs/websockets module. In this article, you’ll learn how to build real-time applications using WebSockets in NestJS, with practical examples and explanations.


Table of Contents

  1. What Are WebSockets?
  2. Why Use WebSockets in NestJS?
  3. Setting Up @nestjs/websockets
  4. Creating a WebSocket Gateway
  5. Handling WebSocket Events
  6. Broadcasting Messages
  7. Client Example with Socket.IO
  8. WebSocket Lifecycle Hooks
  9. Security Considerations
  10. Conclusion

What Are WebSockets?

WebSockets provide a full-duplex communication channel over a single TCP connection. Unlike HTTP, which is request-response based, WebSockets allow real-time interaction with low latency.

Use Cases:

  • Real-time chat
  • Live notifications
  • Gaming servers
  • Collaborative tools (e.g., Google Docs-style editing)

Why Use WebSockets in NestJS?

NestJS makes it incredibly easy to implement WebSockets using:

  • Declarative decorators
  • Modular gateway architecture
  • Built-in support for socket.io and ws engines
  • Integration with DI and NestJS lifecycle

Setting Up @nestjs/websockets

Install the necessary dependencies:

npm install --save @nestjs/websockets @nestjs/platform-socket.io socket.io

If you prefer the ws engine over socket.io, you can replace socket.io with ws.


Creating a WebSocket Gateway

A Gateway in NestJS acts as a WebSocket server. Here’s how to create one.

Step 1: Create a Gateway

// chat.gateway.ts
import {
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
MessageBody,
ConnectedSocket,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({ cors: true }) // or pass custom namespace/port
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server;

handleConnection(client: Socket) {
console.log(`Client connected: ${client.id}`);
}

handleDisconnect(client: Socket) {
console.log(`Client disconnected: ${client.id}`);
}

@SubscribeMessage('message')
handleMessage(
@MessageBody() data: string,
@ConnectedSocket() client: Socket,
) {
console.log(`Message from ${client.id}: ${data}`);
this.server.emit('message', data); // broadcast to all clients
}
}

Step 2: Register in a Module

import { Module } from '@nestjs/common';
import { ChatGateway } from './chat.gateway';

@Module({
providers: [ChatGateway],
})
export class ChatModule {}

Handling WebSocket Events

NestJS maps WebSocket events using the @SubscribeMessage() decorator. These are event-driven, similar to Express route handlers, but asynchronous and long-lived.

@SubscribeMessage('event_name')
handleCustomEvent(@MessageBody() data: any) {
// handle event here
}

Broadcasting Messages

You can emit events to all clients or selectively:

// To all clients
this.server.emit('event', payload);

// To one client
client.emit('event', payload);

// To all except sender
client.broadcast.emit('event', payload);

Client Example with Socket.IO

Here’s a minimal client using the Socket.IO client library.

<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
<script>
const socket = io('http://localhost:3000');

socket.on('connect', () => {
console.log('Connected to WebSocket server');
});

socket.emit('message', 'Hello from client!');

socket.on('message', (msg) => {
console.log('Received:', msg);
});
</script>

WebSocket Lifecycle Hooks

NestJS provides interfaces for connection lifecycle:

HookInterface
On client connectOnGatewayConnection
On client disconnectOnGatewayDisconnect
On init (optional)OnGatewayInit

Example:

handleConnection(client: Socket) {
console.log(`Client connected: ${client.id}`);
}

handleDisconnect(client: Socket) {
console.log(`Client disconnected: ${client.id}`);
}

Security Considerations

  • Authentication: Use middleware to verify JWT on connection.
  • Namespaces: Isolate gateways for different roles/features.
  • Rate Limiting: Prevent flooding and abuse.
  • CORS: Explicitly set CORS policies for WebSocket access.

Example: authenticating on connect

@WebSocketGateway()
export class SecureGateway implements OnGatewayConnection {
handleConnection(client: Socket) {
const token = client.handshake.headers.authorization?.replace('Bearer ', '');
if (!verifyToken(token)) {
client.disconnect();
}
}
}

Conclusion

With the @nestjs/websockets module, NestJS offers a structured and powerful abstraction over WebSocket programming. You can build scalable, real-time applications that integrate seamlessly with the rest of your NestJS ecosystem.

Middleware vs Guard for Auth Handling in NestJS

0
nestjs fullstack course
nestjs fullstack course

When building secure applications with NestJS, authentication plays a central role. Two common mechanisms used for request-level processing are Middleware and Guards. Both can be used for handling authentication, but they serve distinct purposes and operate at different stages of the request lifecycle.

In this module, we’ll explore the key differences between middleware and guards, discuss when and how to use each for authentication, and provide real-world examples for both approaches.


Table of Contents

  1. Understanding the Request Lifecycle in NestJS
  2. What is Middleware in NestJS?
  3. What are Guards in NestJS?
  4. Key Differences Between Middleware and Guards
  5. Using Middleware for Auth
  6. Using Guards for Auth
  7. When to Use Middleware vs Guard
  8. Best Practices
  9. Conclusion

Understanding the Request Lifecycle in NestJS

To decide between middleware and guards, you need to understand where they fit into the NestJS request pipeline:

Middleware → Guards → Interceptors → Controller → Service → Response
  • Middleware runs before route matching
  • Guards run after route matching but before controllers
  • Interceptors run around route handlers (for transformation, logging, etc.)

What is Middleware in NestJS?

Middleware functions are functions that run before your routes are handled. They are typically used for:

  • Logging
  • Request transformation
  • Attaching data to req object
  • Pre-processing headers

They are not route-aware by default and cannot access route metadata (like decorators).


What are Guards in NestJS?

Guards are classes implementing CanActivate and are used for authorization and authentication logic. They are fully aware of the route context and can use route-level metadata (e.g., roles, permissions).

Guards are great for:

  • Route-level access control
  • Protecting routes based on user roles
  • Preventing requests before reaching controllers

Key Differences Between Middleware and Guards

FeatureMiddlewareGuard
Execution TimeBefore route handlingAfter route match, before controller
Access to Route Metadata❌ No✅ Yes
Use CasesLogging, headers, basic parsingAuth, RBAC, request authorization
Return Value BehaviorNo return value control flowMust return true or throw error
Dependency Injection (DI)❌ Limited (via @Injectable workaround)✅ Full DI support

Using Middleware for Auth

Let’s say you want to attach a decoded JWT token to every request.

Step 1: Create the middleware

// auth.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import * as jwt from 'jsonwebtoken';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const token = req.headers['authorization']?.replace('Bearer ', '');

if (token) {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req['user'] = decoded;
} catch (err) {
// do not throw here — middleware shouldn’t block the request
}
}

next();
}
}

Step 2: Apply in AppModule or specific module

// app.module.ts
import { MiddlewareConsumer, Module } from '@nestjs/common';

@Module({ /* imports and providers */ })
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
}
Middleware cannot prevent route execution, which is a critical limitation for secure auth.

Using Guards for Auth

For secure route protection, use Guards with dependency injection.

Step 1: Create a guard

// jwt-auth.guard.ts
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import * as jwt from 'jsonwebtoken';

@Injectable()
export class JwtAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization']?.replace('Bearer ', '');

if (!token) {
throw new UnauthorizedException('Token missing');
}

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
request.user = decoded;
return true;
} catch (err) {
throw new UnauthorizedException('Token invalid');
}
}
}

Step 2: Apply the guard to routes

@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Req() req) {
return req.user;
}

When to Use Middleware vs Guard

SituationUse MiddlewareUse Guard
You need to parse token and attach to req
You need to protect a route
You need to read route decorators (roles)
You want to log all incoming requests
You want to apply global auth filter✅ (light) + ✅ Guard

Best Practices

  • Use middleware for lightweight pre-processing (like decoding JWTs, logging).
  • Use guards for anything related to authorization, roles, or blocking requests.
  • Use combination: middleware can decode the token, guard can verify access based on route metadata.
  • Avoid throwing exceptions inside middleware — handle errors gracefully.

Conclusion

Both middleware and guards are powerful tools in NestJS, but they solve different problems. Middleware is ideal for preprocessing, while guards provide route-aware access control. For robust authentication systems, especially where roles and permissions are involved, guards are the go-to mechanism.