This post is part of the Azure Static Web Apps with Blazor series.
A complete working example is available here: mongeon/code-examples · azure-swa-blazor/05-local-dev-cli.
In article 3, we worked around the local development problem by configuring the HttpClient to point directly to the Azure Functions port. That works, but it doesn’t reproduce SWA’s production behavior: the proxy, the /api/ routing, protected routes, authentication. The swa CLI exists to solve all of that.
Installing the tools
You need two things:
# The Static Web Apps CLI
npm install -g @azure/static-web-apps-cli
# Azure Functions Core Tools (if not already installed)
npm install -g azure-functions-core-tools@4
Check that everything is in place:
swa --version
func --version
The swa start command
The swa CLI acts as the SWA proxy in production. It listens on http://localhost:4280 and forwards requests to your Blazor app and your Functions API. All requests to /api/* go through the proxy, exactly like in production.
The basic command for our project:
swa start http://localhost:5000 --run "dotnet run --project Client.csproj" --api-location Api
This does three things in a single command. First, it launches your Blazor project with dotnet run and waits for it to be available on port 5000. Then, it starts Azure Functions Core Tools in the Api folder. Finally, it starts the SWA proxy on http://localhost:4280 that ties them together.
You access your app on http://localhost:4280, not on localhost:5000. That’s the proxy port that reproduces SWA’s behavior.
Using swa init for configuration
Rather than typing the full command every time, you can initialize a config file:
swa init
The CLI will ask a few questions about your project and generate a swa-cli.config.json file at the root:
{
"$schema": "https://aka.ms/azure/static-web-apps-cli/schema",
"configurations": {
"azure-swa-blazor": {
"appLocation": ".",
"apiLocation": "Api",
"outputLocation": "wwwroot",
"appDevserverUrl": "http://localhost:5000",
"run": "dotnet run --project Client.csproj",
"apiDevserverUrl": ""
}
}
}
After that, a simple swa start is enough. The CLI reads the config and launches everything correctly.
Authentication emulation
This is probably the most useful CLI feature for development. When you hit /.auth/login/github through the local proxy, the CLI doesn’t send you to GitHub. Instead, it shows a form that lets you simulate a login by choosing a username, a provider and roles.
This means you can test your protected pages and your <AuthorizeView> components without configuring real authentication providers. The /.auth/me endpoint returns the simulated user’s info, and the x-ms-client-principal header is injected into API requests, exactly like in production.
To test different roles, log out via /.auth/logout, then log back in specifying the roles you want in the form. Very handy for validating that your protected routes and role logic work correctly.
The staticwebapp.config.json file locally
The CLI respects your staticwebapp.config.json. Protected routes, redirects, headers, everything is enforced locally. If you configured an /admin/* route that requires the authenticated role, the CLI will block access if you’re not “signed in” through the emulation.
If the CLI can’t find your config file, specify its location with --swa-config-location:
swa start http://localhost:5000 --api-location Api --swa-config-location Client/wwwroot
Running the API separately
Sometimes it’s handy to run Azure Functions in a separate terminal to see its logs more clearly. In that case, start Functions yourself:
cd Api
func start --port 7071
And point the CLI to the already running server:
swa start http://localhost:5000 --api-location http://localhost:7071
When --api-location receives a URL instead of a folder path, the CLI knows it should proxy to an existing server rather than start one.
Debugging in Visual Studio
If you use Visual Studio, you can configure the solution to launch both projects at once. Right-click the solution > Configure Startup Projects > Multiple startup projects, and set both Client and Api to “Start”.
Visual Studio will launch both projects in debug. You can set breakpoints in your Blazor code and in your Functions. However, you lose the SWA proxy. To get the proxy alongside debugging, run swa start in a separate terminal pointing to the Visual Studio ports.
An approach that works well: launch both projects in debug from Visual Studio, then in a terminal:
swa start http://localhost:5000 --api-location http://localhost:7071
You keep debugging with breakpoints and you get the SWA proxy with auth emulation.
What can trip you up
The Blazor port can change. Check Properties/launchSettings.json in your Client project to see which port it listens on. If it’s 5292 instead of 5000, adjust the swa start command accordingly.
The CLI downloads Functions Core Tools automatically if it can’t find them, which can be surprising the first time. It’s generally better to install the Core Tools yourself to control the version.
Static files in development. If you use --run with dotnet run or dotnet watch, the CLI proxies to the Blazor dev server and serves files dynamically. No need to dotnet publish first.
Hot reload works. If you use dotnet watch run instead of dotnet run, changes in your Razor files are automatically reflected in the browser through the proxy.
In the next and final article of the series, we’ll explore preview environments: how each pull request generates a complete environment with its own URL.
Articles in this series
- What is an Azure Static Web App?
- The staticwebapp.config.json file
- Adding an Azure Functions API
- SWA’s built-in authentication
- Local development with the swa CLI (This article)
- PR preview environments
Happy deploying, and if port 4280 is already taken, the CLI will tell you pretty clearly.
This post was written with AI assistance and edited by me.