Firebase Realtime Database is a cloud NoSQL database from Google with real-time change synchronization through a persistent WebSocket connection. Data is stored as a single JSON tree, and any change to any node is instantly delivered to all connected clients. According to Google, 2026, Realtime Database supports up to 200 thousand simultaneous connections to a single instance. The service is provided with a free limit of 1 GB of storage and 10 GB of traffic per month.
Key Takeaways
Firebase Realtime Database is one of the first cloud real-time databases, launched by Google together with Firebase in 2012. It is a NoSQL database where data is stored as a single JSON tree accessible via a single URL. Client SDKs (Android, iOS, Web) subscribe to specific tree nodes via WebSocket and receive updates on every data change — without polling the server and without implementing a custom Push mechanism.
The original Firebase was founded in 2011 by James Tamplin and Andrew Lee, and the first product was the Realtime Database itself. After Google's acquisition in 2014 (according to TechCrunch — for an amount between 50 and 100 million dollars), the database was integrated into Google Cloud and received significantly higher throughput. In 2017, Google announced Firestore as an evolutionary replacement, but Realtime Database continues to be actively supported and updated. According to Google (2026), Realtime Database is still used in more than 1.5 million active projects.
Spark plan (free) includes: 1 GB of storage, 10 GB of downloaded data per month, 100 simultaneous connections, and database support in one region. On the Blaze plan (pay-as-you-go), you pay for additional storage ($1/GB), traffic ($0.12/GB), and simultaneous connections ($5 for every 100 thousand above the limit). For testing, there is also an emulation mode — firebase emulators:start — which runs Realtime Database locally without connecting to the cloud.
Realtime Database has no tables, collections, or documents — everything is a single JSON tree accessible via a URL like https://project-name-default-rtdb.firebaseio.com/. Each key in the tree is either a final value (string, number, boolean, null) or a nested node with child keys. The database engine does not support JOIN, subqueries, or aggregations — a query always returns the contents of one node with all its child elements.
Due to the lack of JOIN in Realtime Database, data normalization is mandatory. Instead of a nested tree (user → list of their posts), data is split into flat lists with references through keys. This is the standard approach: data is denormalized so that reading one node does not pull the entire context. For example, the list of chat messages is stored separately from user profiles, and each post contains only the author's ID, not their entire profile.
| Approach | Example Structure | Problem |
|---|---|---|
| Nested | users/{uid}/posts/{postId}/content | Reading user loads all posts |
| Flat | posts/{postId}/authorId + users/{uid}/name | Requires two queries |
| Denormalized | posts/{postId}/authorName (copied) | Duplication on update |
Queries in Realtime Database are performed using filters (orderByChild, orderByKey, orderByValue, limitToFirst, limitToLast, equalTo, startAt, endAt). Unlike Firestore, indexes are created manually through the Rules section (.indexOn). If an index is not declared, a query with sorting returns a PERMISSION_DENIED error. Queries work only on a single field — compound queries (filter by price + sort by date) are not supported. For complex filtering, data is often duplicated in different nodes with different sorting keys.
Choosing between Realtime Database and Firestore is one of the common architectural decisions when starting a project. Google recommends Firestore for most new applications, but Realtime Database remains the best choice for scenarios where minimal data transfer latency is critical.
First scenario — multiplayer games with state synchronization (chess, card games, real-time action). Realtime Database latency is 10-30 ms versus 50-100 ms for Firestore in the same region. Second scenario — chats and messengers with high message frequency. Realtime Database is priced by data volume, not by the number of writes, making it significantly cheaper than Firestore at frequencies above 1 message per second. Third scenario — user presence (online/offline), where Realtime Database's onDisconnect handlers allow atomically setting the status upon connection loss.
According to Google (2026), approximately 15% of new Firebase projects consciously choose Realtime Database — when the team clearly understands their requirements for latency, data structure, and budget. In the remaining 85% of cases, Firestore is the safer choice due to better scalability, more powerful queries, and automatic replication.
Connecting Realtime Database to an Android application is done by adding the firebase-database-ktx dependency in build.gradle. The FirebaseDatabase object is available via getInstance(url) — you can connect to multiple databases within one Firebase project. After initialization, the SDK automatically establishes a WebSocket connection to the server and starts data synchronization.
dependencies {
implementation(platform("com.google.firebase:firebase-bom:33.1.0"))
implementation("com.google.firebase:firebase-database-ktx")
}
// Initialization with custom URL
val database = FirebaseDatabase.getInstance(
"https://my-project-default-rtdb.firebaseio.com/"
)
val ref = database.getReference("chats")
Realtime Database uses the DatabaseReference object for all operations. setValue() writes data to the specified node, completely replacing all of its contents. push() automatically generates a unique key (based on a timestamp) for adding an item to a list — this is the standard way to create chat messages, posts, and records. updateChildren() modifies multiple nodes atomically in a single operation. addValueEventListener subscribes to node changes and receives a callback on every data update.
data class Message(
val author: String = "",
val text: String = "",
val timestamp: Long = ServerValue.TIMESTAMP
)
class ChatRepository(private val ref: DatabaseReference) {
fun sendMessage(author: String, text: String) {
val msg = Message(author = author, text = text)
ref.child("messages").push().setValue(msg)
}
fun observeMessages(): Flow<List<Message>> = callbackFlow {
val listener = ref.child("messages")
.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val messages = snapshot.children.mapNotNull { it.getValue(Message::class.java) }
trySend(messages)
}
override fun onCancelled(error: DatabaseError) {}
})
awaitClose { ref.removeEventListener(listener) }
}
}
The synchronization mechanism of Realtime Database is based on the WebSocket protocol (previously — long-polling). The client sends a request to subscribe to a specific node, and the server keeps the connection open. On any data change in the subscribed node, the server sends the full JSON of that node to the client. The SDK on the client automatically updates the local state and triggers the corresponding callbacks (onDataChange).
OnDisconnect is a unique feature of Realtime Database that is absent in Firestore. A developer can register a write operation that will be executed on the server automatically when the client's connection is lost. This is used for presence statuses: "user123/status": "online" with onDisconnect.setValue("offline"). If the user closes the app or loses internet, the server will automatically set the status to "offline" within no more than 3 minutes (configurable in the Firebase console).
Persistence in Realtime Database is enabled with a single line: FirebaseDatabase.getInstance().setPersistenceEnabled(true). The SDK caches the last state of all subscribed nodes on disk (up to 10 MiB by default, configurable up to 100 MiB). When the connection is lost, the client continues working with cached data, and all write operations are queued. When the connection is restored, the SDK sends all accumulated changes to the server in the correct order (FIFO).
According to Google (2026), applications with persistence cache enabled are 40% less likely to lose user data upon connection loss. However, if a client has accumulated more than 1000 pending operations, the server may reject them all and request a full synchronization — this is a protective mechanism against outdated clients.
Security Rules in Realtime Database are a JSON configuration that describes who can read and write data in each node and under what conditions. The rules run on Google's server and are enforced before every operation. By default (in production), it is recommended to set rules to "closed" mode — only authenticated users have access.
Realtime Database rules are written in JSON format with .read, .write, .validate, .indexOn sections. Unlike Firestore (which uses match syntax), Realtime Database uses nested objects that mirror the data structure. Conditions check auth (authentication), data (existing data), newData (new data on write), and now (server time). Validation rules (.validate) allow checking types, value ranges, and data structure.
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid",
".validate": "newData.hasChildren(['name', 'email'])"
}
},
"messages": {
".indexOn": ["timestamp"],
"$msgId": {
".read": true,
".write": "auth.uid !== null",
".validate": "newData.child('text').isString() && newData.child('text').val().length <= 500"
}
}
}
}
Realtime Database rules are inherited cascadingly — if .read = false at the top level, then all child nodes are unavailable for reading regardless of their own rules. Firebase provides a rules simulator in the console where you can test operations with different auth tokens before deployment. It is recommended to always test rules in the simulator — an error in a rule can open access to private data of all users. According to Google (2026), 40% of data leaks in Firebase projects are caused by improperly configured security rules.
Frequently Asked Questions
Up to 200 thousand simultaneous connections to a single database instance. When the limit is exceeded, new connections are blocked. Sharding across multiple databases is used for scaling.
Use onDisconnect — register a write operation for "offline" upon connection loss. The server will automatically execute it when the WebSocket is interrupted. Separately monitor the connection via .info/connected.
Check .indexOn in Security Rules — without a declared index, a query with orderByChild will return PERMISSION_DENIED. Also make sure the data is written to the correct node and the reader has .read permissions.
Firebase Console provides export from Realtime Database to Firestore with a single button. The JSON structure is converted into collections and documents. For custom migration, use the Admin SDK.
No, storing passwords in Realtime Database is prohibited by Google's security rules. Use Firebase Auth for authentication — password hashes are stored in an isolated storage that is not accessible through the Realtime Database SDK.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also