Create a Static web app with a serverless API
Learn how to locally run then deploy a static web app with a serverless API to Azure. This tutorial uses the preview version of the latest Azure Functions Node.js programming model. Because this article uses a preview version of Azure Functions, it is deployed as a separate app from the static web app.
Learn how to:
- Locally run a static web app (SWA) with an Azure Function app
- Locally proxy front-end requests to local back-end API using the SWA CLI.
- Deploy and run the same code remotely.
The proxy between the front-end and backend-endis provided by the Static web app CLI provides:
- The URL in React,
/api/todo
, doesn't specify the server or port number for the API. Requests using this URL are successful locally because the SWA CLI manages the proxy for you. - A local authentication emulator when accessing
/.auth/login/<provider>
- Route management and authorization
Authentication in this sample
The authentication in this sample is provided for front-end users from the Azure Static Web Apps service:
- Login/Logout
- Public and private content
Source code in this sample
The source code in this sample is meant to learn how to build and deploy a static web app with a serverless API. The code isn't meant for production.
You'll find several places in the code that don't follow best security practices. For example, the code uses console.log
to write to the browser console.
When you move to a production environment, you should review and remove any code, which violates security best practices for your organization.
1. Prepare your development environment
Create the following accounts:
- Azure subscription - Create a free Azure account
- GitHub account - You need a GitHub account to deploy in this tutorial.
Install the following on your local development computer:
- Node.js v18+
- Visual Studio Code (VS Code)
- Azure Static Web Apps (SWA) CLI installed globally with
-g
flag - Azure Functions Core Tools v4.0.5095+ (if running locally) installed globally with
-g
flag - TypeScript v4+
2. Fork the sample repository on GitHub
You need to have your own fork of the sample repository to complete the deployment from GitHub. During the fork process, you only need to copy the main
branch.
Fork the sample repository: https://github.com/Azure-Samples/azure-typescript-e2e-apps
.
3. Clone the forked sample repository
In a bash terminal, clone your forked repository to your local computer. Don't clone the original sample repository. An example URL is
https://github.com/YOUR-ACCOUNT-NAME/azure-typescript-e2e-apps
git clone YOUR-FORKED-REPO-URL
Install dependencies for the local front-end app:
cd app-react-vite && npm install
Install dependencies for the local back-end app:
cd ../api-inmemory && npm install && cd ..
4. Optional, build and run local app
The sample repository has several versions of the front-end and backend apps. The following steps use the React 18 (Vite) version of the front-end and the Azure Function v4 with Node.js version of the back-end with the /status
and /todo
API routes.
From the root of the sample app, use the SWA CLI with the
./swa-cli.config.json
file to build the front-end and back-end apps:swa build
If you run into errors, which may happen depending on the version of various packages and your environment, fix the errors before continuing. It's important to know that your project successfully builds locally before moving on to deployment to Azure Static Web Apps.
From the root of the sample app, use the SWA CLI to start the apps with a proxy.
swa start
When you see the following lines in the bash terminal, the project successfully started.
[swa] Serving static content: [swa] /workspaces/azure-typescript-e2e-apps/app-react-vite/dist [swa] [swa] Serving API: [swa] /workspaces/azure-typescript-e2e-apps/api-inmemory [swa] [swa] Azure Static Web Apps emulator started at http://0.0.0.0:4280. Press CTRL+C to exit.
Open a web browser to the proxied URL,
http://localhost:4280
. You should see the following page:You can sign in using authentication provided by the SWA CLI. The process mocks authentication in cloud-based Azure Static web apps. The front-end code uses the
/.auth/me
endpoint to get the user's identity. Enter any fake user name and don't change the rest of the fields.Once a user is authenticated, the front-end displays private information such as the API's environment variables.
The Azure Function v4 app source code for this API is:
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; import { name, version } from '../../package.json'; function isObject(v) { return '[object Object]' === Object.prototype.toString.call(v); }; function sortJson(o){ if (Array.isArray(o)) { return o.sort().map(sortJson); } else if (isObject(o)) { return Object .keys(o) .sort() .reduce(function(a, k) { a[k] = sortJson(o[k]); return a; }, {}); } return o; } export async function status(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> { context.log(`Http function processed request for url "${request.url}"`); const sortedEnv = sortJson(process.env); return { jsonBody: { name, version, env: sortedEnv, requestHeaders: request.headers }}; }; app.http('status', { route: "status", methods: ['GET'], authLevel: 'anonymous', handler: status });
Expand the public and private sections to see the content from the API is displayed.
5. Create a new Azure Functions app
The previous section of running the static web app with the API was optional. The remaining sections of the article are required to deploy the app and API to the Azure cloud.
To use the preview version of the Azure Functions v4 runtime, you need to create a new Azure Functions app. Your static web app also needs to be rebuilt and redeployed to use the Azure Functions app URI in the Fetch requests to the API instead of using a proxied and managed API.
In a web browser, open the Azure portal to create a new Azure Functions app: Create new app
Use the following information to create the Function App::
Tab:Setting Value Basics: Subscription Select the subscription you want to use. Basics: Resource Group Create a new resource group such as first-static-web-app-with-api
. The name isn't used in the app's public URL. Resource groups help you group and managed related Azure resources.Basics: Instance details: Function App name Enter a globally unique name such as swa-api
with 3 random characters added at the end, such asswa-api-123
.Basics: Instance details: Code or container Select Code
.Basics: Instance details: Runtime stack Select Node.js
.Basics: Instance details: Runtime stack Select 18LTS
.Basics: Operating system Select Linux
.Basics: Hosting Select Consumption
.Storage: Storage account Don't change this. A new Azure Storage account is created to help with function events. Networking Don't change anything. Monitoring: Application Insights: Enable Application Insights Select Yes
. Don't change the default name provided.Deployment: GitHub Actions Settings: Continuous deployment Select Enable
.Deployment: GitHub account Select your GitHub account. Deployment: Organization Select your GitHub account, which you used when you forked the sample repository. Deployment: Repository Select your forked repository name, azure-typescript-e2e-apps
.Deployment: Branch Select main
.Tags Don't change anything. Review + create Select Create
.The step adds a GitHub yaml workflow file to your forked repository.
When the resource is created, select the
Go to resource
button.Select Settings -> Configuration then add a configuration setting for the Azure Function Node.js v4 runtime with name
AzureWebJobsFeatureFlags
and valueEnableWorkerIndexing
.Select Save to save the setting.
In a bash terminal, use git to pull down the new yaml workflow file from your GitHub forked repository to your local computer.
git pull origin main
In Visual Studio Code, open the new yaml workflow file located at
./.github/workflows/
.The default workflow file provided for you assumes the function source code is at the root of the repository and is the only app in the repository but that isn't the case with this sample. To fix that, edit the file. The lines to edit are highlighted in the following yaml block and explained below:
# Docs for the Azure Web Apps Deploy action: https://github.com/azure/functions-action # More GitHub Actions for Azure: https://github.com/Azure/actions # Deploy Azure Functions Node.js v4 runtime # with api-inmemory subdir name: Azure Function App - api-inmemory on: push: branches: - main paths: - 'api-inmemory/**' workflow_dispatch: env: AZURE_FUNCTIONAPP_PACKAGE_PATH: 'api-inmemory' # set this to the path to your web app project, defaults to the repository root NODE_VERSION: '18.x' # Azure Functions v4 runtime requires 18 VERBOSE: true # For debugging jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: 'Checkout GitHub Action' uses: actions/checkout@v2 - name: Setup Node ${{ env.NODE_VERSION }} Environment uses: actions/setup-node@v1 with: node-version: ${{ env.NODE_VERSION }} - name: 'Resolve Project Dependencies Using Npm' shell: bash run: | pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' npm install npm run build --if-present npm run test --if-present popd - name: 'Upload artifact for deployment job' # For debugging uses: actions/upload-artifact@v3 with: name: azure-function-v4-app path: | ${{env.AZURE_FUNCTIONAPP_PACKAGE_PATH}} !${{env.AZURE_FUNCTIONAPP_PACKAGE_PATH}}/node_modules !${{env.AZURE_FUNCTIONAPP_PACKAGE_PATH}}/dist - name: 'Run Azure Functions Action' uses: Azure/functions-action@v1 id: fa with: app-name: 'swa-api' # change this to your Azure Function app name slot-name: 'Production' package: ${{env.AZURE_FUNCTIONAPP_PACKAGE_PATH}} publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_123 }} scm-do-build-during-deployment: false enable-oryx-build: false
Property change Purpose name
Shorten the name so you can easily find it in your fork's GitHub actions list. paths
Add the paths section to limit the deployment to run only when the Azure Functions API code changes. When you edit the workflow file, you can trigger the deployment manually. AZURE_FUNCTIONAPP_PACKAGE_PATH
When using a subdirection for source code, this needs to be that subdirectory path and name. VERBOSE
This setting is helpful for debugging the build and deploy process. step named Upload artifact for deployment job
This step creates a downloadable artifact. This is helpful when debugging exactly what files are deployed to your Azure Function. The
Upload artifact for deployment job
is optional. It's used to understand and debug what files are deployed to Azure Functions or to use those files in a separate environment.Save the file then add, commit, and push it back to GitHub with git:
git add . git commit -m "fix the workflow for a subdir" git push origin main
From a browser, rerun the workflow on GitHub in your fork's actions area.
Wait for the action to successfully complete before continuing.
In a web browser, use your function app's external API endpoint to verify the app deployed successfully.
https://YOUR-FUNCTION-APP-NAME.azurewebsites.net/api/todo
The JSON result returned for the in-memory data is:
{ "1": "Say hello" }
Make a note of your function's URL. You need that in the next section.
You know your Azure Function app is working in the cloud. Now you need to create your static web app in the cloud to use the API.
6. Create a new Azure Static web app
This creation process deploys the same forked GitHub sample repository to Azure. You configure the deployment to use only the front-end app.
Open the Azure portal and sign in with your Azure account: Azure portal.
Use the following information to complete the creation steps:
Prompt Setting Subscription Select the subscription you want to use. Resource Group Select Create new
and enter a new for the resource group such asfirst-static-web-app
. The name isn't use in the app's public URL. Resource groups help you group resources used for a single project.Hosting plan type Select Free
Azure Functions and staging details Don't change the default. You aren't deploying the Function API within the static web app. Deployment details - source Select GitHub
Deployment details - GitHub Sign in to GitHub if necessary. Deployment details - Organization Select your GitHub account. Deployment details - Repository Select the forked repository named azure-typescript-e2e-apps
.Deployment details - Branch Select the main
branch.Build details - Build Presents Select Custom
.Build details - App location Enter /app-react-vite
.Build details - Api location Leave empty Build details - Output location Enter the location of the front-end's output directory, dist
.Select Review + create, then select Create.
When the resource is created, select the
Go to resource
button.On the Overview page, make a note of your static web app's URL. You need that in the next section when you set the Azure Function's CORS setting.
The creation process creates a GitHub yaml workflow file in your forked GitHub repository. Pull that change down with the following command:
git pull origin main
The GitHub action found at
./.github/workflows/azure-static-web-apps-*.yml
is responsible for building and deploying the front-end app. Edit the file to add an environment variable for the cloud-based back-end API URL. The lines to edit are highlighted in the following yaml block and explained below the yaml block.name: Azure Static Web Apps CI/CD on: push: branches: - main paths: - 'app-react-vite/**' pull_request: types: [opened, synchronize, reopened, closed] branches: - main paths: - 'app-react-vite/**' workflow_dispatch: jobs: build_and_deploy_job: if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v2 with: submodules: true - 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_ORANGE_DUNE_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: "/app-react-vite" # 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: VITE_BACKEND_URI: https://swa-api-123.azurewebsites.net VITE_CLOUD_ENV: production 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_ORANGE_DUNE_123 }} action: "close"
Property change Purpose paths
Add the paths section to limit the deployment to run only when the Azure Functions API code changes. When you edit the workflow file, you can trigger the deployment manually. workflow_dispatch
Add workflow_dispatch
only while learning the deployment process and debugging any issues in the Vite build. Remove this line, when you continue this source code beyond this article.if ... || github.event_name == 'workflow_dispatch'
Include the workflow_dispatch
event as allowed to generate a build only while learning the deployment process and debugging any issues in the Vite build.env
Add the environment variables necessary to include the Azure Function API's URL in the static build with Vite.VITE_BACKEND_URL is the URL of your Azure Function app. VITE_CLOUD_ENV is a parameter to indicate when to use the VITE_BACKEND_URL URL. Don't use NODE_ENV for this sample as it has unintended side affects. Save the file then add, commit, and push it back to GitHub with git:
git add . git commit -m "fix the workflow for a subdir" git push origin main
From a browser, rerun the workflow on GitHub in your fork's actions area for your static web app.
Your front-end app is deployed to Azure. Now you need to configure the Azure Function app to allow CORS requests from your static web app.
7. Configure CORS for your Azure Function app
When using a separate Azure Function app, instead of a managed Function app, you need to configure CORS to allow requests from your static web app.
- In the Azure portal, open your Azure Function app.
- In the API -> CORS section, add your static web app's URL to the list of allowed origins.
8. Test your static web app
- In a browser, open your static web app.
- Interact with the app to sign in, view public and private information, and sign out again.
9. Clean up all resources used in this article series
Clean up all resources created in this article series.
- In the Azure portal, delete your resource group, which deletes the static web app and the function app.
- In the GitHub portal, delete your forked repository.
Troubleshooting
This sample keeps a list of known issues and resolutions. If your issue isn't listed, please open an issue.
Static web app and function app public URLs
You can always find your static web app's URL and your function app's URL in the Azure portal, on each resource's Overview page. These URLs are public by default.