Realtime Database
Real-time synchronization, presence detection, and offline support for your Blazor WebAssembly applications.
Overview
Firebase Realtime Database is a cloud-hosted NoSQL database that lets you store and sync data between your users in real-time. FireBlazor provides a type-safe, intuitive API for working with Realtime Database in your Blazor WebAssembly applications.
Key features include:
- Real-time synchronization - Data is synced across all clients in milliseconds
- Presence detection - Track user online/offline status
- Offline support - Data is persisted locally and synced when connectivity is restored
- Server values - Timestamps and atomic increments handled server-side
- Transactions - Atomic read-modify-write operations
Firebase Realtime Database automatically caches data locally and handles reconnection seamlessly. Your app will continue to work offline, and changes will be synced when connectivity is restored. This is handled automatically - no additional configuration required.
Basic Operations
FireBlazor provides a clean, fluent API for all standard Realtime Database operations. All operations return a Result<T> type for functional error handling.
Set Data
Use SetAsync to write or replace data at a specific location. This will overwrite any existing data at that path.
// Set data at a specific path
await Firebase.RealtimeDb.Ref("users/user1").SetAsync(userData);
// Example with a user object
var user = new User
{
Name = "John Doe",
Email = "john@example.com",
CreatedAt = DateTime.UtcNow
};
await Firebase.RealtimeDb.Ref("users/user123").SetAsync(user);
Push Data
Use PushAsync to add data with an auto-generated unique key. This is ideal for lists where you want Firebase to manage unique identifiers.
// Push data with auto-generated key
var pushResult = await Firebase.RealtimeDb.Ref("messages").PushAsync(message);
// Get the generated key
if (pushResult.IsSuccess)
{
var newKey = pushResult.Value; // e.g., "-NxYz123abc"
Console.WriteLine($"Created message with key: {newKey}");
}
Get Data
Use GetAsync<T> to read data from a specific location. The data is automatically deserialized to your specified type.
// Get data with type-safe deserialization
var result = await Firebase.RealtimeDb.Ref("users/user1").GetAsync<User>();
if (result.IsSuccess && result.Value != null)
{
var user = result.Value;
Console.WriteLine($"Welcome, {user.Name}!");
}
Update Data
Use UpdateAsync to update specific fields without overwriting the entire object. Only the specified fields are modified.
// Update specific fields
await Firebase.RealtimeDb.Ref("users/user1").UpdateAsync(new { Status = "online" });
// Update multiple fields at once
await Firebase.RealtimeDb.Ref("users/user1").UpdateAsync(new
{
Status = "online",
LastActive = DateTime.UtcNow,
LoginCount = 5
});
Remove Data
Use RemoveAsync to delete data at a specific location. This removes the node and all its children.
// Remove data at a path
await Firebase.RealtimeDb.Ref("users/user1").RemoveAsync();
// Remove with result handling
var result = await Firebase.RealtimeDb.Ref("messages/oldMessage").RemoveAsync();
if (result.IsSuccess)
{
Console.WriteLine("Message deleted successfully");
}
Real-time Listeners
One of the most powerful features of Realtime Database is the ability to listen for data changes in real-time. Use OnValue to subscribe to changes at a specific location.
// Subscribe to real-time updates with query options
var unsubscribe = Firebase.RealtimeDb
.Ref("messages")
.OrderByChild("timestamp")
.LimitToLast(50)
.OnValue<Dictionary<string, Message>>(
snapshot => {
if (snapshot.Exists)
_messages = snapshot.Value.Values.ToList();
InvokeAsync(StateHasChanged);
},
error => Console.WriteLine(error.Message));
// Later, when component is disposed:
unsubscribe();
Available query methods:
OrderByChild(string childKey)- Order results by a child keyOrderByKey()- Order results by keyOrderByValue()- Order results by valueLimitToFirst(int limit)- Limit to the first N resultsLimitToLast(int limit)- Limit to the last N resultsStartAt(object value)- Start at a specific valueEndAt(object value)- End at a specific valueEqualTo(object value)- Filter to a specific value
Always store the unsubscribe function and call it when your component is disposed to prevent memory leaks. Consider using FirebaseComponentBase which handles this automatically with the Subscribe() method.
Server Values
Server values are placeholders that are resolved on the Firebase server. This ensures consistency across clients and eliminates issues with client clock differences.
ServerValue.Timestamp
Use ServerValue.Timestamp to set a field to the server's current timestamp. This is more reliable than using client-side timestamps.
// Set server timestamp
await Firebase.RealtimeDb.Ref("posts/post1").UpdateAsync(new {
CreatedAt = ServerValue.Timestamp
});
// Use in a new object
await Firebase.RealtimeDb.Ref("events").PushAsync(new
{
Type = "user_login",
UserId = currentUserId,
Timestamp = ServerValue.Timestamp
});
ServerValue.Increment
Use ServerValue.Increment for atomic counter operations. This ensures accurate counting even with concurrent updates.
// Atomic increment
await Firebase.RealtimeDb.Ref("counters/visits").SetAsync(ServerValue.Increment(1));
// Decrement by using negative value
await Firebase.RealtimeDb.Ref("inventory/item1/stock").SetAsync(ServerValue.Increment(-1));
// Increment by any amount
await Firebase.RealtimeDb.Ref("users/user1/points").SetAsync(ServerValue.Increment(100));
Transactions
Transactions allow you to perform atomic read-modify-write operations. This is essential when you need to update data based on its current value, especially with concurrent users.
// Transaction for atomic updates
var result = await Firebase.RealtimeDb
.Ref("likes/post1")
.TransactionAsync<int>(current => (current ?? 0) + 1);
if (result.IsSuccess)
{
Console.WriteLine($"New like count: {result.Value}");
}
Transactions are ideal for:
- Incrementing/decrementing counters
- Toggling boolean values
- Appending to arrays
- Any update that depends on the current value
// More complex transaction example
var result = await Firebase.RealtimeDb
.Ref("games/game1/players")
.TransactionAsync<List<string>>(currentPlayers =>
{
var players = currentPlayers ?? new List<string>();
if (!players.Contains(currentUserId))
{
players.Add(currentUserId);
}
return players;
});
Presence Detection
Presence detection allows you to track when users are online or offline. This is built on Firebase's OnDisconnect feature, which executes operations when a client disconnects.
Setting Online Status
Mark a user as online when they connect to your application.
// Set online status
await Firebase.RealtimeDb.Ref($"presence/{userId}").SetAsync(new { Status = "online" });
OnDisconnect for Offline Status
Use OnDisconnect to automatically update the user's status when they disconnect (close the browser, lose connection, etc.).
// Set offline status on disconnect
await Firebase.RealtimeDb
.Ref($"presence/{userId}")
.OnDisconnect()
.SetAsync(new { Status = "offline", LastSeen = ServerValue.Timestamp });
Complete presence system example:
// Full presence detection setup
public async Task SetupPresence(string userId)
{
var presenceRef = Firebase.RealtimeDb.Ref($"presence/{userId}");
// Set online status
await presenceRef.SetAsync(new
{
Status = "online",
LastSeen = ServerValue.Timestamp
});
// Configure offline status for when user disconnects
await presenceRef
.OnDisconnect()
.SetAsync(new
{
Status = "offline",
LastSeen = ServerValue.Timestamp
});
}
Firebase automatically handles reconnection when a user's connection is temporarily lost. The OnDisconnect operations are only executed when the connection is truly terminated, not during brief network interruptions.