Skip to main content

Overview

Horse Trust uses Socket.io to provide instant, bidirectional communication between buyers and sellers. The chat system includes conversation management, real-time message delivery, typing indicators, and read receipts.

Architecture

The real-time chat system is built on three main components:

Conversations

Two-participant channels linked to horse listings

Messages

Persistent chat messages with read status

Socket.io Server

WebSocket server for real-time events

Data Models

Conversation Model

Defined in server/src/models/VetRecord.ts:
Conversations are strictly two-participant to maintain clear buyer-seller communication.

Message Model

Messages have a 2000 character limit to prevent abuse and ensure proper display.

Last Message Snapshot

Conversations store a snapshot of the last message for quick list rendering:

Socket.io Server Setup

The Socket.io server is configured in server/src/index.ts:

CORS Configuration

string | string[]
Comma-separated list of allowed origins from CORS_ORIGINS environment variable
boolean
default:true
Allow credentials (cookies, authorization headers) in cross-origin requests

JWT Authentication Middleware

All Socket.io connections must be authenticated:
1

Extract Token

Client passes JWT token in socket.handshake.auth.token
2

Verify Signature

Token is verified against JWT_SECRET environment variable
3

Attach User Data

Decoded user data is attached to socket instance for use in handlers
4

Reject Unauthorized

Invalid or missing tokens result in connection rejection

Connection Handling

User Rooms

Each authenticated user automatically joins a personal room:
User rooms enable targeted notifications without iterating through all connected sockets.

Event Handlers

Join Conversation

Users join conversation-specific rooms to receive messages:

Send Message

The core messaging logic with full error handling:
Verify the user is actually a participant in the conversation before allowing message send.
Messages are immediately persisted to MongoDB before being broadcast.
The conversation’s last_message field is updated for efficient list rendering.
Messages are sent to both the conversation room (for open chats) and recipient’s personal room (for notifications).
The callback function confirms successful delivery to the sender.

Typing Indicators

Real-time typing indicators enhance the chat experience:
Note the use of socket.to() instead of io.to() - this broadcasts to all sockets in the room except the sender.

Client-Side Integration

Connecting to Socket.io

Joining a Conversation

Sending Messages

Receiving Messages

Typing Indicators

Read Receipts

Implement read receipts by emitting a read event when messages are viewed:

Disconnection Handling

Client-Side Reconnection

Best Practices

Authentication Required

Always verify JWT tokens before allowing socket connections

Participant Validation

Check user is authorized to send messages in a conversation

Database First

Persist messages before broadcasting to ensure durability

Acknowledgments

Use acknowledgment callbacks for critical operations

Room Management

Properly join/leave rooms to prevent memory leaks

Error Handling

Gracefully handle all errors with user feedback

Performance Considerations

Load messages in batches (e.g., 50 at a time) and implement infinite scroll for long conversations.
Throttle typing indicator events to reduce unnecessary broadcasts (max once per second).
Use connection pooling for database queries in high-traffic scenarios.
For horizontal scaling, use Redis adapter to enable cross-server communication:

Next Steps

Message History API

Load conversation history via REST API

Notification System

Implement push notifications for offline users