I built nestjs-socket-presence — a drop-in NestJS module for real-time user presence tracking (Socket.IO + Redis). Nothing like this existed on npm, so I made it.
Hey r/nestjs 👋
I've been building omnichannel chat platforms for 5+ years and one thing I always had to implement from scratch on every project was user presence tracking — who's online, who's offline, which agents are available in a room.
There was no dedicated npm package for this in the NestJS ecosystem. So I built one.
---
**nestjs-socket-presence** — https://www.npmjs.com/package/nestjs-socket-presence
Install:
npm install nestjs-socket-presence ioredis
---
**What it does:**
- Automatic online/offline tracking on socket connect/disconnect
- Multi-socket per user (one user, multiple tabs/devices — single presence state)
- Redis TTL expiry — ungraceful disconnects auto-expire (no ghost users ever)
- Heartbeat support — clients refresh TTL on a timer
- Room presence — who's online in a specific room right now
- Bulk presence queries — check hundreds of users in one Redis round-trip
- Multi-instance safe — works behind a load balancer
- register() and registerAsync() — works with ConfigService
---
**Usage is dead simple:**
// app.module.ts
PresenceModule.register({
redis: { host: 'localhost', port: 6379 },
ttl: 30,
})
// Client
const socket = io('http://localhost:3000', {
auth: { userId: 'user-123' }
})
// That's it. User is now tracked as online.
// Inject PresenceService anywhere
const online = await this.presenceService.isOnline('user-123');
const room = await this.presenceService.getRoomPresence('support-room');
const bulk = await this.presenceService.getBulkPresence(['u1', 'u2', 'u3']);
---
**Why Redis?**
So it works across horizontally scaled NestJS pods. One user connects to pod A, another to pod B — presence state is shared via Redis. No ghost users, no split-brain.
---
GitHub: https://github.com/SaifuddinTipu/nestjs-socket-presence
npm: https://www.npmjs.com/package/nestjs-socket-presence
22 unit tests. Full TypeScript types. MIT license.
Feedback and PRs very welcome — this is v1.0.0 and I want to make it production-solid.