A normalized PostgreSQL schema for a real-time messaging platform.
The features that are kept in mind while making this database design are:
Note: Authentication is handled by a separate auth microservice. This document focuses on the messaging-system database schema for storing chat data and user profiles.
Schema diagram
contacts table.chats table.messages and message_recipients tables.chats table in detailThe chats table is for managing one-to-one conversations with per-user settings:
interface Chat {
sender_id: number // User initiating the chat
receiver_id: number // User receiving the chat
cleared_at: Date | null // When this user cleared the chat
archived: boolean // User archived the chat
pinned: boolean // User pinned the chat
}
We need two entries per conversation. This is because User A's settings for chat with User B are independent from User B's settings for chat with User A. User A might archive while User B views it actively.
The cleared_at Column: This timestamp is key for the "clear chat" feature. When a user clears their chat history, instead of deleting messages (which would affect the other user), we record the timestamp. Messages created after cleared_at are shown; messages before are hidden from this user only.
message_recipients table in detailinterface MessageRecipient {
id: number
message_id: number // Foreign key to message
receiver_id: number // User who should receive this
status: 'sent' | 'delivered' | 'read' // Read receipt status
created_at: Date // When the message was created
updated_at: Date // When status was last updated
}
For a group message sent to 5 members, 5 message_recipient records are created - one per member. This allows:
The entity relationships for a normalized data design: