Publishing and subscribing with Redis
Redis has a built-in pub/sub mechanism. Publishers send messages to a channel. Subscribers listen on the channel and receive every message. It is fire-and-forget: if nobody is subscribed when the message is published, the message is gone.
import { Redis } from 'ioredis';
let _publisher: Redis | null = null;
function getEventPublisher(): Redis {
if (!_publisher) _publisher = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
return _publisher;
}
export async function publishDomainEvent(eventName: string, payload: unknown) {
try {
await getEventPublisher().publish(eventName, JSON.stringify(payload));
} catch (err) {
console.warn('Could not publish ' + eventName + ': ' + (err as Error).message);
}
}The order service publishes a domain event with a small payload.
import { Redis } from 'ioredis';
import { db } from '../db';
import { inventory } from '../db/schema';
import { eq, sql } from 'drizzle-orm';
// IMPORTANT: subscriber connections cannot run normal commands.
// Always use a dedicated connection for subscribing.
const subscriber = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
export function setupEventSubscriptions() {
subscriber.subscribe('OrderPlaced');
subscriber.on('message', async (channel, message) => {
if (channel !== 'OrderPlaced') return;
const payload = JSON.parse(message);
await db
.update(inventory)
.set({ stockQuantity: sql`stock_quantity - ${payload.quantity}` })
.where(eq(inventory.bookId, payload.bookId));
console.log('Inventory updated for book ' + payload.bookId);
});
}The inventory service subscribes to OrderPlaced and deducts stock.
One Redis quirk worth remembering. A connection that is subscribed to a channel cannot run normal commands like GET or SET. Redis switches the connection into subscriber mode and refuses everything else. Always use a dedicated connection for subscribing. Keep another for publishing and regular reads.
Quiz: Quiz
Loading practiceโฆ