The Firebase Realtime Database (RTDB) is a database provided by Firebase for storing and synchronizing data between users in real time. It's a NoSQL cloud storage database that lets you store and synchronize data across different devices without the need to write complex server code.
Main features of Firebase Realtime Database
- Based on JSON
- Data in the RTDB is stored in JSON format, making the data structure quite flexible. You can easily add, modify, and delete data without needing to change the schema as in relational databases.
- Developers can directly manipulate data in the database using standard JSON manipulation methods, simplifying interactions.
- Data synchronization in real time
- One of the most attractive aspects of the RTDB is automatic data synchronization. When data changes, all connected clients receive these changes in real time.
- This is particularly useful for apps where multiple users may interact with the same data simultaneously, such as in chats or multiplayer games.
- Offline data access
- The Firebase Realtime Database provides the capability to store data locally on the device, allowing your apps to function even without internet access.
- As soon as the device reconnects to the internet, all local changes will be automatically synchronized with the cloud, and vice versa—any changes in the cloud will be delivered to the device.
Basics of Working with Realtime Database in Kotlin
To start using the Realtime Database in your project, add dependencies to your build.gradle.kts:
dependencies {
// Import the BoM for the Firebase platform
implementation(platform("com.google.firebase:firebase-bom:32.2.2"))
// Add the dependency for the Realtime Database library
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation("com.google.firebase:firebase-database-ktx")
}
To start working with a database, first get a reference to it:
val database = FirebaseDatabase.getInstance().reference
Here's an example of writing data:
val user = User("Anna", "[email protected]")
database.child("users").child(userId).setValue(user)
Here, User is a simple class with some fields.
Below is an example of reading data:
val userReference = database.child("users").child(userId)
userReference.get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
One of the advantages of the Realtime Database is the ability to listen for data changes in real time. This can be done using ValueEventListener or ChildEventListener.
Here's an example of subscribing to changes using ValueEventListener:
val userListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val user = dataSnapshot.getValue(User::class.java)
Log.i("firebase", "User name: ${user?.name}, email: ${user?.email}")
}
override fun onCancelled(databaseError: DatabaseError) {
Log.w("firebase", "loadUser:onCancelled", databaseError.toException())
}
}
userReference.addValueEventListener(userListener)
To unsubscribe from updates, simply remove the listener:
userReference.removeEventListener(userListener)Security and Access Rules for Realtime Database
Security rules are a JSON structure that determines which operations are allowed for specific paths in your database. They contain keys such as .read and .write, which define read or write permissions for specific paths, respectively. Below are several examples of rules for different access types.
- Open access (not recommended)
Here, everyone can read and write data. Do not use such rules in production!
{
"rules": {
".read": true,
".write": true
}
}
- Private access
Now, no one, except authenticated users, can read or write data.
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
- Role-based access
You might have specific roles in your database: for example, administrators and users.
{
"rules": {
"adminContent": {
".read": "root.child('admins').child(auth.uid).exists()",
".write": "root.child('admins').child(auth.uid).exists()"
},
"userContent": {
".read": "auth != null",
".write": "auth.uid === $user_id"
}
}
}
- Time-limited access
In this case, users can add data but only until a certain date.
{
"rules": {
"limitedTimeContent": {
".write": "now < 1672531200000" // for example, until January 1, 2023
}
}
}
When working with Firebase security rules, it's crucial to test thoroughly and ensure that your data is appropriately protected. While the rules allow for flexible data access configurations, mistakes in them can lead to vulnerabilities.
Working with Complex Queries in Realtime Database in Kotlin
The Realtime Database provides several methods for sorting and filtering data.
- Sort by key
val reference = FirebaseDatabase.getInstance().getReference("path_to_data") reference.orderByKey().addListenerForSingleValueEvent(/* ... */) - Sort by value
reference.orderByValue().addListenerForSingleValueEvent(/* ... */) - Filtering data
If you want to get only certain items, use thestartAt(),endAt(), orequalTo()methods.
reference.orderByChild("child_name")
.startAt("value")
.endAt("value")
.addListenerForSingleValueEvent(/* ... */)
For pagination, you can use the limitToFirst(n) and limitToLast(n) methods:
- Limit on the initial items
reference.limitToFirst(10).addListenerForSingleValueEvent(/* ... */) - Limit on the last items
reference.limitToLast(10).addListenerForSingleValueEvent(/* ... */)
Integration with other Firebase Components
- Firebase Authentication for user identification
Firebase Authentication offers several methods for authenticating users, including email and password, Google, Facebook, and more. After successful authentication, you can obtain the user's unique identifier (UID), which can be used to create unique paths in the Realtime Database.
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val user = FirebaseAuth.getInstance().currentUser
val uid = user?.uid
// Use uid to work with user data in Realtime Database
}
}
- Firebase Storage for media file storage
Firebase Storage provides the ability to upload and download media files. After uploading a file, you can get its URL and save it to the Realtime Database.
val storageReference = FirebaseStorage.getInstance().getReference("path/to/storage")
storageReference.putFile(uri).addOnSuccessListener { taskSnapshot ->
val downloadUrl = taskSnapshot.storage.downloadUrl
downloadUrl.addOnSuccessListener { uri ->
val fileUrl = uri.toString()
// Save the file URL in the Realtime Database
}
}Conclusion
The Firebase Realtime Database (RTDB) is a tool that provides developers with the ability to store and synchronize data in real time. Like any tool, RTDB has its advantages and disadvantages, and depending on your project's needs, other alternatives may be preferable. When choosing, it is important to take into account the specifics of your project and the requirements for the database.