Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

32 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Zolo – Chat Application

A scalable, production-ready chat backend built with Node.js, Express, TypeScript, and MongoDB, designed using clean architecture principles and industry-standard messaging patterns.

This README documents all the work completed so far, the design decisions, and flow diagrams to clearly explain how the system works.

πŸ“Œ Features Implemented (So Far)

βœ… JWT-based Authentication (protected routes)

βœ… Conversations

  • 1:1 (DM) conversations
  • Group conversations
  • Group admins support

βœ… Messages

  • Send messages
  • Fetch messages with pagination
  • Conversation-level lastMessage tracking

βœ… Unread Message System (Industry Standard)

  • Per-user read tracking
  • Unread count per conversation
  • Works for DM and Group chats
  • Pagination-safe

🧱 Core Data Models

1️⃣ User

Stores basic user details (name, avatar, etc.).

2️⃣ Conversation

Represents a chat (DM or Group).

Key Fields:

  • type: dm | group
  • participants: User IDs
  • admins: User IDs (group only)
  • lastMessage: Message ID

3️⃣ Message

Represents a single chat message.

Key Fields:

  • conversationId
  • sender
  • content
  • type (text / image / file)
  • createdAt

Messages are immutable.

4️⃣ ConversationRead (MOST IMPORTANT)

Tracks read state per user per conversation.

Meaning:

For a given user and conversation, what is the last message the user has read?

Key Fields:

  • conversationId
  • userId
  • lastReadMessageId

This model enables scalable unread message logic.

🧠 Core Design Principle (Unread Messages)

❌ Messages are NOT individually marked as read

βœ… Each user maintains a read pointer (bookmark) per conversation

A message is considered read if:

message._id <= lastReadMessageId

Unread messages are derived, not stored.

πŸ” Flow Diagrams

1️⃣ Send Message Flow

User
  β”‚
  β–Ό
Auth Middleware
  β”‚
  β–Ό
sendMessageService
  β”‚
  β”œβ”€ Validate conversation
  β”œβ”€ Validate participant
  β”‚
  β–Ό
Create Message
  β”‚
  β–Ό
Update Conversation.lastMessage
  β”‚
  β–Ό
Return message

πŸ“Œ Note:

  • No unread state updated here
  • Message writes stay fast

2️⃣ Fetch Messages (Mark as Read Flow)

User opens chat
  β”‚
  β–Ό
GET /messages/:conversationId
  β”‚
  β–Ό
fetchMessagesService
  β”‚
  β”œβ”€ Validate conversation & participant
  β”‚
  β–Ό
Fetch messages (paginated)
  β”‚
  β–Ό
Find ConversationRead
  β”‚   β”œβ”€ If not exists β†’ create
  β”‚
  β–Ό
Update lastReadMessageId (latest message)
  β”‚
  β–Ό
Return messages

πŸ“Œ This is the only place where messages are marked as read.

3️⃣ Fetch Conversations (Unread Count Flow)

User opens chat list
  β”‚
  β–Ό
GET /conversations
  β”‚
  β–Ό
getConversationsService
  β”‚
  β–Ό
Fetch all user conversations
  β”‚
  β–Ό
For each conversation:
  β”‚
  β”œβ”€ Get ConversationRead
  β”œβ”€ Build unread query
  β”‚     β”œβ”€ Same conversation
  β”‚     β”œβ”€ Sender != user
  β”‚     └─ Message > lastReadMessageId
  β”‚
  β–Ό
Count unread messages
  β”‚
  β–Ό
Attach unreadCount
  β”‚
  β–Ό
Return conversation list

🧩 Data Relationship Diagram

User
  β”‚
  β–Ό
Conversation
  β”‚
  β–Ό
Message
  β–²
  β”‚
ConversationRead
  • Conversation is shared
  • Message is immutable
  • ConversationRead is user-specific

πŸ“Š Unread Count Formula

Unread Messages =
  Messages where:
    - conversationId matches
    - sender != current user
    - message._id > lastReadMessageId

If lastReadMessageId is null β†’ all messages from others are unread.

πŸ§ͺ Example Timeline

A sends m1
B sends m2
B sends m3
A opens chat β†’ lastRead = m3
B sends m4

Unread count for A:

1 (m4)

Unread count for B:

0

⚑ Real-Time Messaging Architecture (Socket.IO)

Design Rules

  • REST APIs create data
  • Socket.IO delivers events
  • Clients never send messages via socket
  • Server emits socket events after DB persistence

🏠 Socket Room Strategy

Conversation Room

conversation:<conversationId>

Used for:

  • Real-time messages
  • Typing indicators (future)
  • Read receipts (future)

Joined when:

  • User opens a conversation

Left when:

  • User switches conversation

User Room

Used for:

  • Notifications
  • Unread updates
  • Presence (future)

Joined on:

  • Socket connection

πŸ” Real-Time Message Flow

Sender

Send message β†’ REST API
β†’ DB save
β†’ REST response
β†’ Sender UI updates immediately

Receiver

socket.on("message:new")
β†’ append message to state
β†’ UI updates instantly

βš™οΈ Technical Highlights

  • Pagination-safe unread logic
  • No per-message read flags
  • One DB write per chat open
  • Works for DM & Group chats
  • Real-time messaging using Socket.IO

πŸ”œ Next Planned Steps

  • Real-time unread updates
  • Aggregation-based unread optimization
  • Read receipts ("Seen by X")
  • Group admin permissions

🏁 Conclusion

This backend implements an industry-grade chat architecture similar to WhatsApp / Slack:

  • Clean separation of concerns
  • Scalable unread message design
  • With real-time features

🎨 Frontend – Work Completed So Far

πŸ” Authentication UI

πŸ”— Frontend ↔ Backend Integration

Frontend does not access refresh tokens directly.

🧠 Frontend Architecture

βš™οΈ Technical Highlights

πŸ”œ Next Planned Steps

Backend

  • Socket.IO real-time messaging
  • Redis-backed unread optimization
  • Read receipts (β€œSeen by”)
  • Group permission enforcement

Frontend

🏁 Conclusion

Zolo implements an industry-grade chat architecture with:

πŸ‘¨β€πŸ’» Built as part of the Zolo Project

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages