Інтеграція Firebase Realtime Database для чату у мобільному застосунку
Firebase Realtime Database дає WebSocket-з'єднання з коробки, синхронізацію в реальному часі та простий SDK — це чесні плюси для прототипу або малого чату. Але при зростанні навантаження або ускладненні структури починаються специфічні проблеми, які розв'язуються правильною структурою даних з першого дня.
Структура даних — ключове рішення
Найдорожча помилка при інтеграції Firebase Realtime Database для чату — плоска структура з вкладеними повідомленнями в об'єкт чату. Коли в розмові 10 000 повідомлень, кожен childEventListener на кореневий вузол чату тягне все дерево. На Android це виражається в OutOfMemoryError, на iOS — помітна затримка при відкритті старого чату.
Правильна структура:
/chats/{chatId}/
metadata: { title, lastMessage, updatedAt }
members: { userId1: true, userId2: true }
/messages/{chatId}/{messageId}/
text, senderId, timestamp, status
/userChats/{userId}/{chatId}: true
Розмежування метаданих чату та повідомлень дозволяє підписатися на список чатів користувача (/userChats/{userId}) без завантаження всієї історії. Повідомлення завантажуються окремо з пагінацією через limitToLast(50).
Пагінація та real-time оновлення
Поєднання початкової завантаження через limitToLast та live-підписки на нові повідомлення — не тривіальна задача. Стандартний підхід:
- Завантажуємо останні 50 повідомлень через
orderByChild("timestamp").limitToLast(50). - Запам'ятовуємо
timestampнайстарішого повідомлення з набору. - Live-підписка на нові повідомлення після поточного часу:
startAt(currentTimestamp). - Для завантаження історії вверх:
endAt(oldestTimestamp).limitToLast(50)— новий одноразовий запит.
Android SDK:
val query = database.child("messages").child(chatId)
.orderByChild("timestamp")
.startAt(System.currentTimeMillis().toDouble())
query.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
val message = snapshot.getValue(Message::class.java) ?: return
// додаємо в список
}
// ...
})
Правила безпеки
Firebase Security Rules — обов'язкова частина, яку часто роблять останньою. Без правильних rules база відкрита. Мінімальний набір для чату:
{
"rules": {
"messages": {
"$chatId": {
".read": "auth != null && root.child('chats').child($chatId).child('members').child(auth.uid).exists()",
".write": "auth != null && root.child('chats').child($chatId).child('members').child(auth.uid).exists()"
}
}
}
}
Тестування правил — через Firebase Rules Playground до деплою в продакшн.
Обмеження та коли вибрати Firestore
Realtime Database програє Firestore за можливостями запитів: немає складених індексів, немає where з кількома полями. Якщо потрібна сортування повідомлень за кількома критеріями, пошук по тексту або складна фільтрація — Firestore переважніший. Для простого чату «один-на-один» або групового без складної логіки Realtime Database цілком підходить.
Що входить в роботу
Проектуємо схему даних під ваш тип чату, реалізуємо інтеграцію SDK (Android/iOS/Flutter), налаштовуємо пагінацію та live-оновлення, пишемо Security Rules, налаштовуємо оффлайн-persistence (включена за замовчуванням, але потребує тонкої налаштування keepSynced).
Строка: 3–6 днів для повноцінного чату з історією, online-статусами та статусами прочитання.







