Blazor and Supabase, Real Time - Part 3
Continuation of the article on Blazor and Supabase, in this part we will discover and set up real time
Banner Image
Part 1 : Database
Part 2 : Authentication
Part 3 : Real Time
Supabase also manages real-time functionality. In this article, we’ll explore two ways to use real-time features:
- An event is triggered when a table in the database is modified or created
- Real-time usage via a broadcast, similar to tools like SignalR
I invite you to clone the GitHub repository for the full code: https://github.com/phnogues/Blazor.Supabase/
Prerequisites:
- A Supabase account (free account is sufficient)
- Have Supabase set up as shown in Part 1 of my articles on Supabase
Case 1 - Trigger from the database
1- Enable real-time for the table:
In the relevant table on Supabase’s interface, activate real-time with the "Realtime Off/On" button (Table Editor / The table / button in the top right corner).
2- Code c# :
- To subscribe to an event on the table, in the OnInitializedAsync method:
[Inject]
Supabase.Client _supabase { get; set; }
private RealtimeChannel? _crepesChannel;
protected override async Task OnInitializedAsync()
{
await _supabase.Realtime.ConnectAsync();
// channel for crepes changes
_crepesChannel = _supabase.Realtime.Channel("public:crepes");
if (!_crepesChannel.IsJoined)
{
_crepesChannel.Register(new PostgresChangesOptions("public", "crepes"));
}
_crepesChannel.AddPostgresChangeHandler(PostgresChangesOptions.ListenType.Inserts, CrepeInsertedHandler);
_crepesChannel.AddPostgresChangeHandler(PostgresChangesOptions.ListenType.Updates, CrepeUpdatedHandler);
await _crepesChannel.Subscribe();
}
- The handlers will be called when a crepe is added or deleted :
private void CrepeInsertedHandler(IRealtimeChannel _, PostgresChangesResponse change)
{
var crepe = change.Model<Crepe>();
_notificationService.Notify(NotificationSeverity.Success, "New crepe inserted", $"{crepe.Name}");
Events.Add($"New crepe inserted: {crepe.Name}");
this.InvokeAsync(() => this.StateHasChanged());
}
private void CrepeUpdatedHandler(IRealtimeChannel _, PostgresChangesResponse change)
{
var crepe = change.Model<Crepe>();
_notificationService.Notify(NotificationSeverity.Success, "Crepe updated", $"{crepe.Name}");
Events.Add($"Crepe updated: {crepe.Name}");
this.InvokeAsync(() => this.StateHasChanged());
}
Case 2 : Using message broadcasting
- No setup is needed on Supabase's end (but you can monitor events from the Realtime tab).
- To emit an event:
private RealtimeChannel? _kitchenChannel;
protected override async Task OnInitializedAsync()
{
// Realtime
await _supabase.Realtime.ConnectAsync();
_kitchenChannel = _supabase.Realtime.Channel("order");
if (!_kitchenChannel.IsJoined)
{
_kitchenChannel.Register<Order>(true, true);
}
await _kitchenChannel.Subscribe();
}
// Méthode à appeler depuis un bouton ou autre
private async Task Order(CrepeDto crepe)
{
_kitchenChannel?.Broadcast<Order>()?.Send("order", new Order { Name = crepe.Name, Price = crepe.Price, Date = DateTime.UtcNow }); ;
}
- To listen for the message:
[Inject]
Supabase.Client _supabase { get; set; }
private RealtimeChannel? _orderChannel;
protected override async Task OnInitializedAsync()
{
await _supabase.Realtime.ConnectAsync();
// channel for orders
_orderChannel = _supabase.Realtime.Channel("order");
if (!_orderChannel.IsJoined)
{
_orderChannel.Register<Order>(true, true);
_orderChannel?.Broadcast<Order>()?.AddBroadcastEventHandler(HandleOrderBroadcastReceived);
}
await _orderChannel.Subscribe();
}
public class Order : BaseBroadcast
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("price")]
public float Price { get; set; }
[JsonProperty("date")]
public DateTime Date { get; set; }
}
- The associated handler:
private void HandleOrderBroadcastReceived(IRealtimeBroadcast sender, BaseBroadcast? args)
{
var order = _orderChannel?.Broadcast<Order>()?.Current();
if (order == null) return;
_notificationService.Notify(NotificationSeverity.Warning, "New order ! ", $"{order.Name}");
Events.Add($"New order: {order.Name} {order.Price}€ {order.Date}");
this.InvokeAsync(() => this.StateHasChanged());
}