7 min read

Hosting Blazor Web Assembly App on Azure Static Web App

Justin Yoo

In my previous post, we developed a Blazor Web Assembly app on our local machine. Throughout this post, I'm going to discuss how to deploy the app to Azure Static Web Apps.

The sample code used here can be found at https://github.com/devkimchi/Blazor-React-Sample.

Building Blazor Web Assembly App

Please refer to my previous post about how to build a Blazor Web Assembly app. This post rather focuses on the deployment of the app. The BlazorNpmSample project contains the Blazor app. Build and run this app on your local machine first.

dotnet run -p BlazorNpmSample
view raw 01-dotnet-run.sh hosted with ❤ by GitHub

Open your web browser, go to the website, https://localhost:5001, and navigate to the Counter page, then click the Click me button. You'll be able to find out the screen like below. Don't worry about the error message. It's expected. The error will be sorted out at the end of this post.

Deploying Blazor Web Assembly App to Azure Static Web Apps

At Build 2020, a new Azure Static Web Apps service was announced as a public preview. This service means a lot to front-end development because the front-end applications take responsibilities for user interactions more than ever. Only if necessary, it calls back-end API for data or messages. Especially JAM Stack has become more popular for over the last few years. Therefore, to host those JAM Stack applications, this Azure Static Web App service has been launched. At the time of this writing, it predominantly supports JavaScript-based applications. In order to deploy the Blazor Web Assembly application to the Azure Static Web Apps instance, we need a few extra steps. Let's find them out.

My colleague Tim Heuer wrote an awesome post about this. My post extends his one.

Please refer to the page to deploy an app to Azure Static Web App instance. At the time of this writing, the deployment, in fact, fails.

This is because the action used for deployment, Azure/static-web-apps-deploy@v0.0.1-preview, uses Oryx. Currently, the latest version of .NET Core SDK that Oryx supports is 2.2, which doesn't support Blazor Web Assembly yet. Therefore, we can't 100% rely on the action. The auto-generated GitHub Actions workflow should be accommodated to sort out this issue. Add the following action right after the actions/checkout@v2 action. We need to set the SDK version to 3.1.300 for Blazor Web Assembly (line #4).

- name: Setup .NET SDK
uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.300'

Then, build the Blazor app outside the Oryx action and create the artifact to the published directory (line #4). Of course, we can add more steps before this step, like testing, but let's hold this for now.

- name: Publish Blazor WASM app
shell: bash
run: |
dotnet publish BlazorNpmSample -c Release -o published

We've got the Blazor app. Let's update the existing Build And Deploy step. Change the values of app_location, api_location and app_artifact_location properties (line #6-8).

- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v0.0.1-preview
with:
...
app_location: "published/wwwroot"
api_location: ""
app_artifact_location: "published/wwwroot"

Save the workflow and push it back to the repository. Then the workflow will successfully run, and the Blazor app will be deployed successfully. But there's still the same error occurs. As I mentioned earlier, it's OK for now.

Deploying Proxy API to Azure Static Web Apps

One of the biggest challenges for all static web app hosting services, including Azure Static Web Apps is to communicate with the back-end APIs with no trouble. There are several ways for front-end apps to talk to the back-end APIs. OAuth is one way and API auth key is another. The auth key should be stored in a safe place and used, if necessary. But a static web app is not the place to store the key because it's exposed. Azure Static Web App offers the API proxy feature. With this feature, the auth key is securely stored at rest.

Building External API

First of all, let's build a simple app representing an external API, which is running independently. For now, it's the Azure Functions app written in C#. It takes the count parameter and returns the result. As the BlazorApiSample project contains the source code, build it and run the app with the following command.

func start --script-root BlazorApiSample
view raw 05-api-func-start.sh hosted with ❤ by GitHub

Then, call the API through the web browser.

http://localhost:7071/api/hello?count=1
view raw 06-api-run.txt hosted with ❤ by GitHub

You will be able to see the result like this:

Building Proxy API

Let's build the proxy API for the Azure Static Web Apps instance. As it's still a preview, there are several constraints to consider.

  • It only runs on the node.js runtime.
  • It only supports the HTTP binding.

If you want to know more constraints, please refer to the Constraints page.

Therefore, based on those restrictions, we should build the proxy API. Here's the sample code, BlazorProxySample, which is a really simple one. The following function explains how it works. The proxy API calls the external API (line #9-11) using environment variables of API__BASE_URI, API__ENDPOINT and API__AUTH_KEY (line #4-6). They are not exposed, of course.

const axios = require('axios');
module.exports = async function (context, req) {
let baseUri = process.env.API__BASE_URI;
let endpoint = process.env.API__ENDPOINT
let authKey = process.env.API__AUTH_KEY === undefined ? '' : process.env.API__AUTH_KEY;
let count = req.query.count === undefined ? 0 : req.query.count;
let requestUri = baseUri + endpoint + '?count=' + count + '&code=' + authKey;
let response = await axios.get(requestUri);
let result = { "text": response.data.message };
context.res = {
// status: 200, /* Defaults to 200 */
body: result
};
}

Those environment variables are stored to local.settings.json (line #5-7).

{
...
"Values": {
...
"API__BASE_URI": "http://localhost:7071/api/",
"API__ENDPOINT": "hello",
"API__AUTH_KEY": ""
}
}

Now, all the basic setup is done. Let's run the proxy API. As the external API has already taken the 7071 port, this API should use another port. Here in this post, I use 7072 instead.

func start --port 7072 --script-root BlazorProxySample

You can find out both function instances are running locally at the same time.

If you send a request to the proxy API, it returns the following:

Integrating Blazor App to Proxy API

There is one big difference to consider while working on the local machine. Both Azure Static Web App and the proxy API live in the same instance. Therefore, the deployed app itself has no problem, but they are two different instances at local. The Blazor app runs on https://localhost:5001, and the proxy API runs on http://localhost:7072. To talk to each other, we should set the CORS enabled. Update the local.settings.json file for CORS (line #6-8).

{
...
"Values": {
...
},
"Host": {
"CORS": "*"
}
}

Run the Azure Functions app, proxy API and Blazor app individually (from the right-hand side in the picture).

Navigate the Blazor app to the Counter page and click the Click me button. There's no error! Yay!

Deploying Both Blazor App and Proxy API Altogether

We've got all local development done. Before deployment, we need to adjust the GitHub Actions workflow again. Add the following action straight after the actions/setup-dotnet@v1 action to update appsettings.json that the Blazor app refers to (line #4).

- name: Update appsettings.json
shell: bash
run: |
echo '{ "PROXY_BASE_URI": "/api/", "PROXY_ENDPOINT": "hello " }' > BlazorNpmSample/wwwroot/appsettings.json

And update the Azure/static-web-apps-deploy@v0.0.1-preview to add the proxy API (line #6).

- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v0.0.1-preview
with:
app_location: "published/wwwroot"
api_location: "BlazorProxySample"
app_artifact_location: "published/wwwroot"

Push the change to the GitHub repository, and the change will be published to the Azure Static Web App instance. Run the app, and you will still see the error. That's still fine! Let's fix it.

The error occurs because we haven't set up the environment variables for the proxy API. Add the following environment variables at the Configuration blade.

Refresh the app and click the button. No error! We made it!


So far, we have walked through how the Blazor Web Assembly app is deployed to Azure Static Web App instance. Deploying Blazor app itself only requires a few tweaks on the GitHub Actions workflow. But deploying the proxy API requires more than that, which is a bit complicating. As Azure Static Web App is still in preview, there are a lot of spaces to get improved until becoming GA. I hope, by the time of GA, it will be a lot easier to deploy Blazor Web Assembly app to Azure Static Web App.