Building a Pub/Sub Decorator for NestJS with Dapr
This guide walks through installing the Dapr JavaScript SDK, configuring a RabbitMQ pub/sub component, injecting DaprClient and DaprServer into a NestJS project, creating a custom @DaprPubSubSubscribe decorator, and running a complete end‑to‑end demo that publishes and consumes page‑view events.
Dapr is a portable, event‑driven runtime that enables developers to build resilient stateless or stateful applications for cloud or edge environments without handling distributed‑system complexities.
Install the Dapr JavaScript SDK
Use npm to add the SDK package:
npm install --save @dapr/dapr⚠️ The dapr-client package is deprecated; see https://github.com/dapr/js-sdk/issues/259 for details.
Project setup
Create a new NestJS application and install the Dapr client:
npm install -g @nestjs/cli
nest new api
mv api nest-dapr
cd nest-dapr
nest generate app page-view
npm install dapr-client
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bashInject Dapr dependencies
Register DaprClient in app.module.ts and DaprServer in page-view.module.ts:
providers: [
...,
{ provide: DaprClient, useValue: new DaprClient() }
] providers: [
...,
{ provide: DaprServer, useValue: new DaprServer() }
]Configure RabbitMQ as a Dapr pub/sub component
Start RabbitMQ with Docker Compose:
version: '3.9'
services:
pubsub:
image: rabbitmq:3-management-alpine
container_name: 'pubsub'
ports:
- 5674:5672
- 15674:15672 # web portCreate component/pubsub.yml to define the component:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: default
spec:
type: pubsub.rabbitmq
version: v1
metadata:
- name: host
value: 'amqp://guest:guest@localhost:5674'Each Dapr microservice also needs its own configuration file (e.g., api/dapr/config.yml and page-view/dapr/config.yml).
API/Gateway service
Expose an endpoint that publishes a page‑view event:
@Post('/add-page-view')
async prderAdd(@Body() pageViewDto: PageViewDto): Promise<void> {
try {
console.log(pageViewDto);
await this.daprClient.pubsub.publish('pubsub', 'page-view-add', pageViewDto);
} catch (e) {
console.log(e);
}
}Internal listener microservice
Add a method decorated with the custom @DaprPubSubSubscribe decorator to handle the published message:
@DaprPubSubSubscribe('pubsub', 'add')
addPageView(data: PageViewDto) {
console.log(`addPageView executed with data: ${JSON.stringify(data)}`);
this.data.push(data);
}Implementing the @DaprPubSubSubscribe decorator
In shared/decorators.ts define a map to store subscription metadata and a factory function that registers each subscription with the Dapr server at runtime:
import { INestApplication } from '@nestjs/common';
import { DaprServer } from 'dapr-client';
export type PubsubMap = {
[pubSubName: string]: {
topic: string;
target: any;
descriptor: PropertyDescriptor;
};
};
export const DAPR_PUB_SUB_MAP: PubsubMap = {};
export const DaprPubSubSubscribe = (
pubSubName: string,
topic: string,
): MethodDecorator => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
DAPR_PUB_SUB_MAP[pubSubName] = { topic, target, descriptor };
return descriptor;
};
};
export const useDaprPubSubListener = async (app: INestApplication) => {
const daprServer = app.get(DaprServer);
for (const pubSubName in DAPR_PUB_SUB_MAP) {
const item = DAPR_PUB_SUB_MAP[pubSubName];
console.log(`Listening to the pubsub name - "${pubSubName}" on topic "${item.topic}"`);
await daprServer.pubsub.subscribe(
pubSubName,
item.topic,
async (data: any) => {
const targetClassImpl = app.get(item.target.constructor);
await targetClassImpl[item.descriptor.value.name](data);
},
);
}
};Run the demo
Start RabbitMQ, the API service, and the page‑view listener:
docker-compose up -d # start rabbitmq
npm run dapr:api:dev # start api/gateway
npm run page-view:dev # start internal microservice listening to the rabbitmq queueSend a test request:
curl --location --request POST 'http://localhost:7001/v1.0/invoke/api/method/statistics/add-page-view' \
--header 'Content-Type: application/json' \
--data-raw '{
"pageUrl" : "https://test.com/some-page",
"userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
}'The request triggers the API endpoint, which publishes a message to the Dapr pub/sub component; the custom decorator registers a listener that receives the message and stores the page‑view data, demonstrating a complete end‑to‑end Pub/Sub workflow in a NestJS application powered by Dapr.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
