A proof-of-concept (PoC) real-time notification system with WebSocket push, inbox functionality, read/unread status, and multi-instance support via Redis pub/sub.
- Real-time Push Notifications: Instant delivery via WebSocket (Socket.io)
- Inbox View: Paginated notification history with infinite scroll
- Read/Unread Status: Visual indicators and badge count for unread notifications
- Multi-tab Sync: Notifications sync across multiple browser tabs
- Multi-instance Support: Redis pub/sub for horizontal scaling
- REST API: Full CRUD operations with Swagger documentation
- Professional UI: Modern, responsive design with Tailwind CSS
+------------------+ +-----------------+ +------------------+
| | | | | |
| Next.js Web |<--->| NestJS API |<--->| PostgreSQL |
| (Frontend) | | (Backend) | | (Database) |
| | | | | |
+--------+---------+ +--------+--------+ +------------------+
| |
| WebSocket |
+------------------------+
|
+-------+-------+
| |
| Redis |
| (Pub/Sub) |
| |
+---------------+
- Docker and Docker Compose
- Git
-
Clone the repository
git clone github.com/Springs-Tea/realtime-notification-system cd realtime-notification-system -
Copy environment file
cp .env.example .env
-
Start all services
docker compose up --build
-
Access the application
- Frontend: http://localhost:3000
- API: http://localhost:4000
- Swagger Docs: http://localhost:4000/api/docs
cd backend
cp .env.example .env
npm install
npm run start:devcd frontend
cp .env.example .env
npm install
npm run devNote: You'll need PostgreSQL and Redis running locally or update
.envfiles to point to your instances.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/notifications |
Create a notification |
| GET | /api/v1/notifications |
Get user notifications (paginated) |
| GET | /api/v1/notifications/unread-count |
Get unread notification count |
| POST | /api/v1/notifications/:id/read |
Mark notification as read |
| POST | /api/v1/notifications/read-all |
Mark all notifications as read |
| Event | Direction | Description |
|---|---|---|
subscribe |
Client -> Server | Subscribe to user notifications |
unsubscribe |
Client -> Server | Unsubscribe from notifications |
notification.created |
Server -> Client | New notification received |
notification.updated |
Server -> Client | Notification updated (e.g., marked as read) |
notification.all_read |
Server -> Client | All notifications marked as read |
- Open http://localhost:3000 in your browser
- Click the notification bell icon to open the inbox
- Use the "Send Test Notification" form to create notifications
- Watch notifications appear in real-time
- Open multiple tabs to see cross-tab synchronization
# Create a notification
curl -X POST http://localhost:4000/api/v1/notifications \
-H "Content-Type: application/json" \
-d '{
"userId": "demo-user",
"type": "info",
"title": "Hello World",
"body": "This is a test notification"
}'
# Get notifications
curl http://localhost:4000/api/v1/notifications \
-H "x-user-id: demo-user"
# Get unread count
curl http://localhost:4000/api/v1/notifications/unread-count \
-H "x-user-id: demo-user"realtime-notification-system/
├── backend/ # NestJS Backend
│ ├── src/
│ │ ├── modules/
│ │ │ ├── notifications/ # Notification module
│ │ │ ├── websocket/ # WebSocket gateway
│ │ │ └── redis/ # Redis pub/sub service
│ │ ├── app.module.ts
│ │ └── main.ts
│ ├── Dockerfile
│ └── package.json
├── frontend/ # Next.js Frontend
│ ├── src/
│ │ ├── app/ # App router pages
│ │ ├── components/ # React components
│ │ ├── hooks/ # Custom hooks
│ │ ├── lib/ # Utilities (API, Socket)
│ │ └── types/ # TypeScript types
│ ├── Dockerfile
│ └── package.json
├── compose.yml # Docker Compose config
├── .env.example # Environment template
└── README.md
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| userId | string | User identifier |
| type | enum | info, success, warning, error |
| title | string | Notification title |
| body | text | Notification content |
| readAt | timestamp | When notification was read (nullable) |
| createdAt | timestamp | Creation timestamp |
The system supports horizontal scaling through Redis pub/sub:
- Each API instance subscribes to Redis channels
- When a notification is created, it's published to Redis
- All API instances receive the message and forward it to connected WebSocket clients
- This ensures all users receive notifications regardless of which instance they're connected to
| Variable | Description | Default |
|---|---|---|
DB_HOST |
PostgreSQL host | localhost |
DB_PORT |
PostgreSQL port | 5432 |
DB_USER |
PostgreSQL user | postgres |
DB_PASSWORD |
PostgreSQL password | postgres |
DB_NAME |
Database name | notifications |
REDIS_HOST |
Redis host | localhost |
REDIS_PORT |
Redis port | 6379 |
PORT |
API server port | 4000 |
FRONTEND_URL |
Frontend URL for CORS | http://localhost:3000 |
NEXT_PUBLIC_API_URL |
API URL for frontend | http://localhost:4000/api/v1 |
NEXT_PUBLIC_SOCKET_URL |
WebSocket URL | http://localhost:4000 |
MIT