DSocial is a distributed-based social network platform featuring a community feed, comments, user profiles, real-time notifications, and real-time chat. With local development and self-hosted environments, the services are orchestrated with Docker Compose: a NestJS API, a Socket.IO chat server, a notification worker, and a React frontend.
DSocial is split into 4 runtime components and 4 infrastructure services:
- DSocial Frontend (React + TypeScript + Vite)
- DSocial API (NestJS)
- DSocial Chatting Service (Express + Socket.IO)
- DSocial Notification Worker (NestJS RabbitMQ consumer)
- MongoDB (primary persistence)
- Redis (cache + pub/sub + presence)
- RabbitMQ (notification event bus)
- MinIO (media object storage)
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Vite, Chakra UI, Zustand, Socket.IO Client |
| API Gateway / Backend | NestJS 11, Mongoose, Swagger, Passport JWT, RabbitMQ client, ioredis |
| Chat Service | Express 5, Socket.IO 4, JWT, Mongoose, Redis |
| Notification Worker | NestJS, RabbitMQ subscriber, MongoDB, Redis Pub/Sub |
| Message Broker | RabbitMQ topic exchange (dsocial.notifications) |
| Cache + Realtime | Redis (unread counter, online status, pub/sub channel fanout) |
| Object Storage | MinIO (media upload/download for posts & avatars) |
| Database | MongoDB 6 |
- The frontend calls DSocial API REST endpoints for auth, feed, users, posts, comments, profile, and notifications.
- When an interaction event occurs (for example mention/love/comment), the API publishes a message to the RabbitMQ exchange
dsocial.notifications. - The Notification Worker consumes from the
notification-servicequeue, stores data in MongoDB, updates the Redis unread counter, then publishes to Redis channelnotifications:user:{userId}. - The API exposes SSE endpoint
/notifications/sse, subscribes to Redis patternnotifications:user:*, and forwards real-time events to browsers. - Real-time chat runs separately in
dsocial-chattingvia Socket.IO and REST history endpoints.
- Manages community, login/register, and messenger UI.
- Calls APIs with
Authorization: Bearer <accessToken>. - Opens SSE connection at
/notifications/ssefor real-time notifications. - Connects to the chat service via Socket.IO for real-time messaging.
- Provides REST endpoints:
auth,users,profile,posts,comments,feed,notifications,minio.
- Applies a global JWT guard, except endpoints marked with
SkipAuth. - Uses a transform interceptor to normalize responses (except endpoints marked with
SkipTransform). - Reads/writes data in MongoDB.
- Publishes notification events to RabbitMQ.
- Relays real-time notifications via SSE from Redis pub/sub.
- Handles real-time chat through Socket.IO with JWT auth middleware.
- Provides REST endpoints for user list, profile, chat history, room list, and unread count.
- Stores messages in MongoDB.
- Uses Redis for online presence (
online:{userId}) with TTL refresh.
- Subscribes to RabbitMQ routing key
notification.create. - Persists notifications to MongoDB notifications collection.
- Updates per-user unread counters in Redis (
user:{userId}:unread). - Publishes messages to Redis channel
notifications:user:{userId}.
- The API emits a domain event (post/comment/love/mention).
- The API publishes the event to RabbitMQ topic
notification.create. - The worker consumes the event and stores a notification document in MongoDB.
- The worker increments unread counters in Redis.
- The worker publishes payloads to channel
notifications:user:{consumerId}. - The API SSE service receives pub/sub messages and forwards them to clients via
/notifications/sse. - The frontend shows toast notifications and increments unread badges in real time.
- The frontend calls
/feedto load cursor-based post lists. - The API reads posts, users, and comment summaries from MongoDB.
- The frontend calls refresh/refetch endpoints to sync new data.
- Post/comment interactions trigger corresponding notification events.
- The frontend gets a JWT token at login and connects Socket.IO to the chat service.
- Socket middleware verify token, attach
socket.user. - Users join pair-specific rooms (
{userA}_{userB}). send-messageevents are persisted to MongoDB, then broadcast asreceive-message/new-message.- The frontend can call REST
/api/chat/history/:partnerIdto backfill message history.
POST /auth/registerPOST /auth/login
GET /feed?cursor=&limit=GET /feed/refresh?sinceCursor=GET /feed/refetch?sinceCursor=
GET /users?text=&limit=GET /users/:idGET /users/:id/posts?cursor=&limit=GET /users/:id/posts/refresh?sinceCursor=GET /users/:id/posts/refetch?sinceCursor=GET /profilePUT /profile/bioPOST /profile/avatar(multipart)
GET /postsGET /posts/:idPOST /postsPOST /posts/media(multipart)PATCH /posts/:idDELETE /posts/:idPUT /posts/:id/lovePUT /posts/:id/unlovePOST /commentsPUT /comments/:idDELETE /comments/:id
GET /notificationsGET /notifications/unread-countPATCH /notifications/:id/readPUT /notifications/read-allGET /notifications/sse(Server-Sent Events)
GET /minio/files/:key
GET /api/docs
GET /healthPOST /api/auth/loginGET /api/profileGET /api/usersGET /api/chat/history/:partnerId?page=&limit=GET /api/chat/roomsPOST /api/user/onlineGET /api/user/:userId/onlineGET /api/unread-count
Client -> Server:
join-chat(partnerId)join-room(roomId)send-message({ message, partnerId, _id? })read_receipt({ messageId, roomId, readAt })
Server -> Client:
room-chattedroom-joinedreceive-messagenew-messagesent-messagemessage_read
| Collection | Purpose |
|---|---|
users |
accounts, profiles, roles, avatars |
posts |
community posts |
comments |
comments and replies on posts |
messages |
chat history between user pairs |
notifications |
user-targeted notifications |
| Key / Channel | Type | Meaning |
|---|---|---|
user:{userId}:unread |
string counter | unread notification count |
online:{userId} |
string + TTL | chat online presence |
notifications:user:{userId} |
pub/sub channel | real-time notification channel |
- Notification history is durable in MongoDB.
- Real-time delivery uses Redis pub/sub (non-durable).
- Unread count follows a cache-first strategy, with DB recomputation on cache miss.
- Online presence is TTL-based and may have short-lived drift on abrupt disconnects.
| Service | Port | Note |
|---|---|---|
| dsocial-frontend | 8081 |
frontend container |
| dsocial-api | 3001 |
REST API + SSE + Swagger |
| dsocial-chatting | 7001 |
Socket.IO + chat REST |
| dsocial-noti-worker | internal | RabbitMQ consumer worker |
| mongo | 27017 |
MongoDB |
| redis | 6379 |
cache / pubsub |
| rabbitmq | 5672, 15672 |
broker + management UI |
| minio | 9007, 9008 |
object storage + console |
Run the development stack:
docker compose -f docker-compose.dev.yml up --build -dStop the development stack:
docker compose -f docker-compose.dev.yml downdocker compose -f docker-compose.yml up --build -dnpm run dev
npm run dev:build
npm run start
npm run stop
npm run logs
npm run cleanKey environment variables in the current compose setup:
PORT,PUBLIC_PORT,BASE_URLJWT_SECRETMONGO_URIREDIS_URIRABBITMQ_URIMINIO_ENDPOINT,MINIO_PORT,MINIO_USE_SSLMINIO_ACCESS_KEY,MINIO_SECRET_KEY,MINIO_BUCKET_NAME- Frontend build/runtime:
VITE_API_URLVITE_CHAT_URL
- The API and chat service both use JWT; keep
JWT_SECRETconsistent. - The chat service includes a separate demo login endpoint (
/api/auth/login) for messenger flow. - The SSE notification endpoint requires a Bearer token and sends periodic heartbeat events.
- In local environments, stateful data is mounted under the root
data/directory.
© 2026 Duc Dao ducdmd152 Licensed under the GNU LICENSE.
🤟 Take a star if you find something interesting 🤟