Learn Local Azure Development Using SWA CLI, Core Tools and Azurite

Oct 20, 2026AzureEngineering
Summarize with AI: Google AI Claude ChatGPT Perplexity Grok

AI links open with a title + excerpt (these tools can't fetch the page themselves) — use "Copy full article" to paste the complete text for a fuller summary.

Share:

Introduction

This article demonstrates how to run a full Azure Static Web Apps project locally — the Angular frontend, the Azure Functions API and the storage emulator — so that /api works from the browser exactly as it does in production.

The pieces each run fine on their own. Getting them to behave like one site is the part worth writing down.

The Problem With Running Them Separately

Run ng serve and you get the app on port 4300. Run func start and you get the API on port 7080.

Now your app calls /api/blog and gets a 404, because port 4300 has no API. So you hardcode http://localhost:7080/api/blog, and now you have a CORS problem, and a URL that is wrong in production.

In production there is one origin. Static Web Apps serves your files and routes /api/* to the Functions backend, same domain, no CORS. Your local setup should do the same thing, not work around not doing it.

That is what the SWA CLI is for. It is a proxy that sits in front of both and presents one URL.

Features

  • One URL, same relative /api paths as production.
  • No CORS configuration anywhere.
  • staticwebapp.config.json routing is applied locally too.
  • Hot reload still works.

With the following steps, you can set it up.

  1. Start the storage emulator.
  2. Start everything through the SWA CLI.
  3. Open the right port.
  4. Fix the stale process problem.

Start the Storage Emulator

Azure Functions needs a storage account for its own internal bookkeeping — not for your data, for the host itself. Locally that is Azurite.

"AzureWebJobsStorage": "UseDevelopmentStorage=true"
npm run start:azurite

Under the hood:

azurite --silent --location ../.azurite --skipApiVersionCheck

--location keeps the emulator's files in one folder you can gitignore and delete. Azurite listens on three ports: 10000 for blob, 10001 for queue, 10002 for table.

Start this first, in its own terminal. If you skip it, the Functions host still starts — and then reports itself unhealthy, with errors that do not obviously mention storage. This is the single most common "why is nothing working" moment on this stack, and it has a one-line fix you will never guess from the message.

Your own blob code can point at Azurite too, through AZURE_STORAGE_CONNECTION_STRING. Same SDK, same code, different connection string.

Start Everything Through the SWA CLI

npm start

Which runs:

npx swa start http://localhost:4300 --api-location api --api-port 7080 --port 4290 --run "npm run start:frontend"

Read that command from the inside out.

--run "npm run start:frontend" tells the CLI to start the Angular dev server itself. One command, not two.

http://localhost:4300 is where it expects to find that dev server, matching the port set in frontend/angular.json.

--api-location api points at the Functions folder. The CLI runs Core Tools for you.

--api-port 7080 is where the API will be.

--port 4290 is the combined URL the proxy serves.

You can still run the pieces separately when you need to — npm run start:frontend, npm run start:api — which is useful when you want the API's own output without the proxy's noise.

Open the Right Port

This deserves its own section because everyone gets it wrong once.

Port What
4290 SWA emulator — open this one
4300 Angular dev server
7080 Functions API
10000–10002 Azurite

The Angular CLI prints http://localhost:4300 in your terminal in friendly green text, and that link is wrong. Open it and the app loads — the UI appears, so it looks like it worked — but every API call 404s, because nothing is proxying /api on that port.

Open 4290. Put it in your README in bold. It is the single most common confusion on this stack.

The SWA emulator serving the app and API on one port

Fix the Stale Process Problem

The other recurring annoyance. You stop the app, start it again, and a port is reported unavailable.

Almost always a func.exe or node.exe from the previous run is still holding it. Stopping the SWA CLI does not always take its children with it.

netstat -ano | findstr 7080
taskkill /F /PID <pid> /T

The /T matters — it kills the process tree, not just the one process, which is the whole problem you are solving.

We put those two lines in our architecture notes rather than leaving each person to rediscover them. That is the general lesson from this whole setup: the commands are not hard, but the failure messages point at the wrong thing. A port conflict says "port unavailable", not "your last run is still going". Write down the fix next to the symptom.

A Word on What Local Actually Talks To

One thing to decide deliberately. The emulator is local, but your connection strings might not be.

Ours currently points at production Cosmos DB and a real storage account, even locally. Reads are harmless. But open the admin editor locally and you are editing live posts, with nothing on screen to tell you.

The code is identical either way. Decide which you want, and make sure everyone on the team knows which one they have.

Conclusion

In this article we learned how to run an Angular app, an Azure Functions API and Azurite together behind the SWA CLI, so /api works from one origin exactly as it does in production — and the two things that break it: forgetting Azurite, and a stale process holding a port.

The setup is four processes and one command. The value is that local and production behave the same way, so nothing about your API paths needs an environment check.

Reference

#azure-static-web-apps#swa-cli#azure-functions#azurite#angular#local-development

Comments

Be the first to comment.

Leave a comment

Never shown publicly.

Comments are reviewed before appearing publicly.