Chat Firebase Schema
Chat data usually has two responsibilities: load a user's conversation list quickly, and load the message thread for a selected channel. The schema commonly uses a channel document, a message subcollection, and a denormalized chat feed per participant.
Quick Answer
Store channel metadata under channels/{channelID}, messages under
channels/{channelID}/thread/{messageID}, and each participant's conversation
preview under a per-user chat feed. Keep read receipts, participant ids, last
message metadata, and media references synchronized when messages are sent.
Channel Feed
Common per-user feed path:
social_feeds/{userID}/chat_feed/{channelID}
This feed lets the conversations screen load quickly for one user. It usually contains:
{
id: 'channel-id',
title: 'Conversation title',
content: 'Last message preview',
markedAsRead: false,
createdAt: timestamp,
participants: ['user-a', 'user-b'],
participantProfilePictureURLs: ['https://...']
}
Exact names can vary by product. Search the app and backend:
rg "chat_feed|channels|thread|markedAsRead|readUserIDs" src firebase
Channel Metadata
Common channel path:
channels/{channelID}
Typical fields:
{
id: 'channel-id',
creatorID: 'user-id',
name: 'Group name or empty for 1:1',
participants: ['user-a', 'user-b'],
lastMessage: 'Preview text',
lastMessageSenderId: 'user-a',
lastThreadMessageId: 'message-id',
readUserIDs: ['user-a'],
typingUsers: []
}
For one-to-one chats, many apps generate a deterministic channel id from the two participant ids. This prevents duplicate private channels.
Message Thread
Common message path:
channels/{channelID}/thread/{messageID}
Typical message fields:
{
id: 'message-id',
senderID: 'user-a',
senderFirstName: 'Alex',
senderProfilePictureURL: 'https://example.com/avatar.jpg',
content: 'Hello',
createdAt: timestamp,
readUserIDs: ['user-a'],
inReplyToItem: null,
url: null,
mime: null
}
Media messages usually store a url plus a MIME type or media metadata. The
file itself should live in Firebase Storage or a CDN, not inside Firestore.
Write Flow
When a message is sent, the app or backend should:
- create the message document;
- update channel
lastMessagemetadata; - update each participant's chat feed preview;
- update read/unread state;
- trigger push notification if enabled;
- upload media first if the message contains an attachment.
For large groups or high-volume chat, move fan-out work to backend Functions.
Security Rules
Before production, verify:
- only channel participants can read channel metadata;
- only channel participants can read/write messages;
- users cannot spoof another sender id;
- message media paths are scoped to channel or sender ownership;
- blocked users cannot continue sending messages;
- admin moderation has explicit server-side controls.
Verification Checklist
- User conversations screen loads from the per-user chat feed.
- Opening a conversation loads messages from the channel thread.
- Sending text updates both message thread and conversation previews.
- Sending media uploads the file and stores a valid URL.
- Read receipts update for the current user only.
- Push notifications fire only for intended recipients.
- Block/report behavior is enforced.
Troubleshooting
| Problem | Fix |
|---|---|
| Conversations screen loads forever | Check chat feed query, Auth state, rules, and loading/error handling. |
| Message sends but preview does not update | Check channel metadata and per-user chat feed fan-out. |
| Media message does not render | Check Storage upload, URL, MIME type, and read rules. |
| Duplicate private chats appear | Use deterministic 1:1 channel ids. |
| User sees another user's chat | Tighten participant checks in Firestore rules. |