Swimburger

Real-time applications with Blazor Server and Firestore

Niels Swimberghe

Niels Swimberghe - - DotNet

Follow me on Twitter, buy me a coffee

Blazor and Firebase logo alongside title: Real-time applications with Blazor Server and Firestore

This post was written for and originally posted on the Twilio blog.

Blazor is a framework for building interactive client-side user interfaces for web applications using .NET. Using Blazor, .NET developers can build stylish and performant web interfaces using C# and ASP.NET Core instead of JavaScript.

Blazor Server provides support for hosting Razor components on a server in an ASP.NET Core app. At runtime, the Blazor Server app sends UI events from the browser to the server and applies UI updates sent from the server to the rendered component.

Blazor Server is built on SignalR, which is built on websockets. Among things, websockets enable Blazor Server to push changes from the server to the browser at any time. You can build real time UIs when you combine this with a real-time database such as Google Firestore.

Firestore is Google Firebase's real-time database offering on the Google Cloud Platform (GCP). Google provides .NET libraries for interacting with Firestore. The high-level library for interacting with Firestore is Google.Cloud.Firestore, which can be found on NuGet.

This post will show you how to:

  • Create a Firestore on GCP using the Cloud Shell
  • Integrate Firestore with a .NET Console application
  • Integrate Firestore with Blazor Server to create a real time UI

Important: The Firestore .NET libraries are designed to be run in a server environment with full access/permissions to the database. Client-side technologies—including Blazor WASM—are not supported, nor recommended.

Prerequisites #

This tutorial is for developers at any experience level. Prior experience with the following technologies is recommended, but not required:

  • .NET and ASP.NET Core
  • HTML
  • Blazor
  • Firebase and Firestore

You’ll need the following development resources to build and run the case study project:

There is a companion repository available on GitHub for this tutorial. It contains the complete code for the solution you’re going to build.

Getting started with Firestore #

Once you’re set up with a Google account and Google Cloud Platform, open the Cloud Console, locate the Cloud Shell icon at the top right, and click on it:

A shell will open up in your browser window. Use the Cloud Shell to create a Firestore by executing the upcoming commands to set variables that will be used in upcoming commands:

The PROJECT_ID will be a concatenation of “blazor-sample-” and your username to ensure the ID is universally unique as required by GCP. Project ID’s in GCP also have the following requirements:

The project ID must be a unique string of 6 to 30 lowercase letters, digits, or hyphens. It must start with a letter, and cannot have a trailing hyphen. You cannot change a project ID once it has been created. You cannot re-use a project ID that is in use, or one that has been used for a deleted project. 
— GCP – Creating and managing projects

Update the REGION variable to the region closest to you or where you will host your application. To find all available regions for App Engine (Firestore), run this command:

Run these commands next:

This will create a project that will act as the container to hold together all your cloud resources. The second command will configure the new project as the current project you are working on. Subsequent commands will implicitly receive the new project via the --project argument.

Run the following commands to create an App Engine and Firestore resource:

You'll need to create a service account with a project owner role to authenticate and communicate with Firestore.

Run the following commands to create a service account and assign the project owner role:

Run the following commands to create an authentication file and download it:

This file contains the keys and parameters necessary to authenticate/connect with GCP and Firestore.

You can find a copy of the entire script in the GitHub repository.

Integrating Firestore with a .NET Console application #

Before integrating Firestore with Blazor, verify that you can interact with Firestore using a simple console application.

Run the following commands to create a .NET console application:

Add the Firestore NuGet package:

Update Program.cs with the following code:

Update the variables projectName and authFilePath to match your local environment. The console application will set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of the json-authentication file. Firestore expects this environment variable and will use the file to authenticate to Firestore.

Enter the command dotnet run to try out the application and verify communication with Firestore works as expected.

In summary, here's what the Firestore functions do:

  • FirestoreDb.Create creates a FirestoreDb object which can be used to perform CRUD operations on collections and documents.
  • firestoreDb.Collection("users") creates a reference to the "users" collection. It does not actually query or create the collection in Firestore, instead a CollectionReference is returned even if the collection doesn't exist yet.
  • usersCollection.Document("alovelace") creates a reference to the "alovelace" document. It does not actually query or create the document, instead a DocumentReference is returned even if the document doesn't exist yet.
  • docRef.SetAsync(user) will create the user document if it doesn't exist already. If it does exist, it will update the document.
  • usersCollection.GetSnapshotAsync() will fetch the data from Firestore. The CollectionReference class contains methods to create queries such as OrderByLimitWhere…, etc. These query methods will return a Query object which also contains GetSnapshotAsync. When you call GetSnapshotAsync directly on a collection, it will return all documents in the collection.
  • You can pass an action to the Listen function. The action will be invoked initially with the documents returned by the query/collection. When a change is made to the collection, the action will be called again with the documents returned by the query/collection. It's important to note that the specific changes are not returned, but the entire result of the query is.
  • usersCollection.AddAsync(user); will create a new document and generate a document ID.

When you enter the first and last name at the console prompt you should see the console output from the Listen callback.

Creating a real time UI with Blazor Server and Firestore #

Create a new Blazor server application by running these commands:

Add the Firestore NuGet package:

Remove all .razor files from the Pages folder and create a new file, ChatRoom.razor, in the Pages folder.

Update ChatRoom.razor to match the following content:

This Chatroom page is a dummy version where the Firestore code needs to be implemented.

First, you'll need a FirestoreDb to interact with Firestore. Add the method CreateFirestoreDb in the @code section:

Update the projectName and authFilePath to match your local environment.

Instead of creating documents using Dictionary<string, object>, you can also use typed classes and annotate them with attributes provided by the Firestore library. Update the ChatMessage class to match the content below:

The FirestoreData attribute marks the class as a Firestore document. The FirestoreProperty attribute marks the property to be serialized and stored in Firestore. The ServerTimestamp attribute will auto-populate the property with the Creation/Update time on the server. CreatedAt will be used later to order the messages in a query.

Complete the implementation of the SendMessage method. Create a new ChatMessage instance. Call CreateFirestoreDb and pass the ChatMessage instance to firestoreDb.Collection("chatMessages").AddAsync.

Modify the SendMessage method so it matches the code below:

When you run the Blazor application, you can now create messages, but they won't show up yet. You can verify the messages are created by viewing the data in the Firebase GCP console.

Replace the existing OnAfterRender method with the following code:

To fetch the messages in real time, override the OnAfterRender method and query the "chatMessages" collection. Use LimitToLast and OrderByDescending to only show the last 100 messages sorted by "CreatedAt". Call Listen on the query. It's important that you only call this when the component is rendered for the first time, otherwise you'll create a new listener after every render.

Inside the Listen lambda callback, empty the chatMessages list and add the new messages to the list.

Use the ConvertTo<ChatMessage> method to convert the documents to ChatMessage instances.

Stop the firestoreChangeListener in the Dispose method using StopAsync.

It's important to tell Blazor that the local state has changed, so it can re-render with the updated chatMessages. Doing this directly inside listen will not work because the Listen callback runs on a different thread than renderer's synchronization context.  Using InvokeAsync, you can run the code on the correct thread and within the renderer's synchronization context. Now the Blazor component will re-render the component in real time!

Replace the existing Dispose() method stub with the following code:

Stop the firestoreChangeListener in the Dispose method using StopAsync.

For your reference, here's the code for the entire page:

Now you have a working real-time chat application! 

Note: This sample and tutorial is a proof of concept. Make sure to verify performance and scalability requirements when using Blazor Server with Firestore!

Testing the completed application #

To test the application, execute the dotnet run command on your Blazor Server project. Open the application in a browser. The default URL for local development should be https://localhost:5001.

Open the application in two separate browser windows. You'll notice that when you enter a chat message in one window, the chat message is immediately shown in the other window.

Cleanup cloud resources #

Don't forget to delete your cloud resources to save costs. Use the following Google Cloud Shell command to delete you GCP project and all its resources:

Replace blazor-sample-$USER with the name of your GCP project.

Potential enhancements #

The ChatMessage class is embedded as a nested class for the sake of the demo. It’s recommended to move this class to its own file as done in the  BlazorFirestoreSamples folder of the GitHub repository.

If you want to see more samples, the BlazorFirestoreSamples folder in the GitHub repository contains the same chat application but with some bootstrap styling and a to-do list application. Additionally, the FirestoreDb is injected using dependency injection and configured using AppSettings.json.

Do you want to build a chat app this way? #

Probably not.

Robust, scalable chat applications do a lot more than pass messages back and forth between website users. If you want to integrate a chat solution into your production application take a look at Twilio Programmable Chat. It provides real time data failover, secure messaging, team management, and an elastic infrastructure: the kinds of features you need if your organization is going to rely on chat to meet its sales and operational needs. Check out the list of capabilities and developer documentation to get rolling.

Summary #

In this tutorial you learned how to create GCP cloud resources and, more specifically, to create a Firestore using the Cloud Shell. You also saw how to integrate a .NET Console application with the Firestore database using Google’s Firestore library. You learned how to integrate the Firestore database with Blazor Server to create a real-time UI by building a functional chat application.

Additional resources #

To delve deeper into the topics covered in this post, consult the following references:

Pushing UI changes from Blazor Server to browser on server raised events – This article delves deeper into how Blazor Server pushes its state and UI changes to the browser.

Google’s Firestore .NET documentation – This documentation will provide more details on the .NET Firestore implementation and how to use it to integrate into your projects.

Google’s quickstart using a server client library – A quickstart guide that walks you through using the Firestore with multiple server languages including C#. This guide will use the Google Cloud Console GUI instead of the Cloud Shell to create the Firestore resource.

Firestore documentation – The documentation home of the Firestore documentation to learn about the NoSQL real time database.

Related Posts

Related Posts