Exercise - Use a storage account to host a static website
Now that the API is deployed to the cloud, as a Tailwind Traders engineer, you need to update the client code and deploy it to support the SignalR messages coming for Azure Functions.
Update the client application
In Visual Studio Code, open
./start/client/src/index.js
and replace all the code to listen for SignalR messages. The code gets the initial stock list with an HTTP request and then listens for updates from the SignalR connection. When a stock is updated, the client updates the stock price in the UI.import './style.css'; import { BACKEND_URL } from './env'; const app = new Vue({ el: '#app', data() { return { stocks: [] } }, methods: { async getStocks() { try { const url = `${BACKEND_URL}/api/getStocks`; console.log('Fetching stocks from ', url); const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status} - ${response.statusText}`); } app.stocks = await response.json(); } catch (ex) { console.error(ex); } } }, created() { this.getStocks(); } }); const connect = () => { const signalR_URL = `${BACKEND_URL}/api`; console.log(`Connecting to SignalR...${signalR_URL}`) const connection = new signalR.HubConnectionBuilder() .withUrl(signalR_URL) .configureLogging(signalR.LogLevel.Information) .build(); connection.onclose(() => { console.log('SignalR connection disconnected'); setTimeout(() => connect(), 2000); }); connection.on('updated', updatedStock => { console.log('Stock updated message received', updatedStock); const index = app.stocks.findIndex(s => s.id === updatedStock.id); console.log(`${updatedStock.symbol} Old price: ${app.stocks[index].price}, New price: ${updatedStock.price}`); app.stocks.splice(index, 1, updatedStock); }); connection.start().then(() => { console.log("SignalR connection established"); }); }; connect();
Open
./start/client/index.html
and paste the following code in place of the current DIV with the ID of app.<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css" integrity="sha256-8B1OaG0zT7uYA572S2xOxWACq9NXYPQ+U5kHPV1bJN4=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"> <title>Stock Notifications | Enable automatic updates in a web application using Azure Functions and SignalR</title> </head> <body> <div id="app" class="container"> <h1 class="title">Stocks</h1> <div id="stocks"> <div v-for="stock in stocks" class="stock"> <transition name="fade" mode="out-in"> <div class="list-item" :key="stock.price"> <div class="lead">{{ stock.symbol }}: ${{ stock.price }}</div> <div class="change">Change: <span :class="{ 'is-up': stock.changeDirection === '+', 'is-down': stock.changeDirection === '-' }"> {{ stock.changeDirection }}{{ stock.change }} </span> </div> </div> </transition> </div> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js" integrity="sha256-chlNFSVx3TdcQ2Xlw7SvnbLAavAQLO0Y/LBiWX04viY=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js"></script> <script src="bundle.js" type="text/javascript"></script> </body> </html>
This markup includes a transition element, which allows Vue.js to run a subtle animation as stock data changes. When a stock is updated, the tile fades out and quickly back in to view. This way if the page is full of stock data, users can easily see which stocks have changed.
Add the following script block just above the reference to bundle.js to include the SignalR SDK.
<script src="https://cdn.jsdelivr.net/npm/@aspnet/signalr@1.0.3/dist/browser/signalr.js"></script>
Update the client .env
Create an environment variables file in the
start/client
folder named.env
.Add a variable named
BACKEND_URL
and add its value you copied from unit 5.BACKEND_URL=https://YOUR-FUNTIONS-APP-NAME.azurewebsites.net
Create an Azure Static Web apps resource and deploy client
Open the Azure portal to create a new Azure Static Web Apps resource.
Use the following information to complete the resource creation Basics tab.
Name Value Subscription Select your subscription. Resource group Use the resource group stock-prototype
.Static Web App name Postpend your name to client
. For example,client-jamie
.Hosting plan type Select Free. Deployment source Select GitHub. Organization Select your GitHub account Repository Search and select mslearn-advocates.azure-functions-and-signalr
.Branch Select the main branch. Build Presets Select Vue.js. App location Enter /start/client
.API Location Leave empty. Output Location Enter dist
.Select Preview workflow file to review the deployment settings. The Build And Deploy step should look like the following:
- name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_123 }} repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) action: "upload" ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig app_location: "/solution/client" # App source code path api_location: "" # Api source code path - optional output_location: "dist" # Built app content directory - optional ###### End of Repository/Build Configurations ######
Select Close to save the change.
Select Review + Create, then select Create to create the resource. Wait for the deployment to complete before continuing.
Select Go to resource to open the new Azure Static Web App resource.
On the Overview page, copy the URL value. This is the base URL of the deployed static web app.
Add the BACKEND_URL variable to the repository
The workflow needs to have the BACKEND_URL
environment variable set to the deployed Azure Functions app base URL from unit 5.
In a browser for your GitHub fork of the sample repository, select Settings -> Security -> Secrets and variables -> Actions.
Select Variables, then select New repository variable.
Use the following table to create the variable:
Name Value BACKEND_URL The base URL of the deployed Azure Functions app. The URL should be in the format of https://<FUNCTIONS-RESOURCE-NAME>.azurewebsites.net
Select Add variable to save the variable to the repository.
Edit GitHub deployment workflow
In Visual Studio Code terminal, pull down the new workflow file from your fork (origin).
git pull origin main
Open the
.github/workflows/azure-static-web-apps-*.yml
file.Change the
name
value at the top of the file toClient
.Edit the Build And Deploy step to add the env property for the
BACKEND_URL
environment variable.```yaml name: Client - Azure Static Web Apps CI/CD on: push: branches: - main pull_request: types: [opened, synchronize, reopened, closed] branches: - main jobs: build_and_deploy_job: if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v3 with: submodules: true lfs: false #Debug only - Did GitHub action variable get into workflow correctly? - name: Echo run: | echo "BACKEND_URL: ${{ vars.BACKEND_URL }}" - name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_123 }} repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) action: "upload" ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig app_location: "/solution/client" # App source code path api_location: "" # Api source code path - optional output_location: "dist" # Built app content directory - optional ###### End of Repository/Build Configurations ###### env: BACKEND_URL: ${{ vars.BACKEND_URL }} close_pull_request_job: if: github.event_name == 'pull_request' && github.event.action == 'closed' runs-on: ubuntu-latest name: Close Pull Request Job steps: - name: Close Pull Request id: closepullrequest uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_123 }} action: "close" ```
Save and push the changes to the repository.
git add . git commit -m "Add BACKEND_URL environment variable" git push
Open the Actions tab in the GitHub fork repository to watch the deployment.
Update CORS in the function app
By default, function apps don't allow CORS requests. You need to update the function app to allow requests from the static web app.
In the Azure portal, navigate to the Azure Functions app created in unit 5.
In the left-hand menu, select API -> CORS.
Select Enable Access-Control-Allow-Credentials.
Add the value you copied for the Static Web Apps resource URL.
Property Value Allowed origins The base URL of the deployed static web app. Select Save to save the CORS settings.
Test the deployment of the client
- In a browser, use the URL of the deployed static web app to open the client.
- Open developer tools to watch the Console to see when the SignalR data for updated stock is received. Remember these aren't HTTP requests, so you won't see them in the Network tab.
Congratulations! You've deployed your stock app improved with SignalR!