npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ktarmyshov/svelte-adapter-azure-swa

v1.1.1

Published

SvelteKit adapter for Azure Static Web Apps.

Readme

NPM Version CI Quality Gate Status Coverage License

@ktarmyshov/svelte-adapter-azure-swa

@ktarmyshov/svelte-adapter-azure-swa is a SvelteKit adapter for deploying applications to Azure Static Web Apps.

It prepares the Azure Static Web Apps deployment layout from a SvelteKit build, including:

  • Azure Functions output for the server build
  • static content output
  • generated staticwebapp.config.json
  • SWA-oriented deployment safeguards and diagnostics

It also supports SvelteKit instrumentation, sourcemap-friendly output handling, local SWA platform emulation, and tested compatibility handling for real Azure Static Web Apps / Azure Functions quirks.

Why this adapter

This adapter is focused on practical Azure Static Web Apps deployment rather than only producing nominally compatible output.

Key distinctions:

  • prepares Azure Static Web Apps deployment output from a SvelteKit build
  • generates Azure Functions output and static content output in the layout Azure SWA expects
  • writes staticwebapp.config.json with SWA-safe defaults and routing safeguards
  • supports the SvelteKit instrumentation contract
  • uses a Rolldown-based output pipeline with sourcemap-friendly rebundling
  • supports local SWA platform emulation for App.Platform-dependent code
  • includes Azure-specific compatibility handling, diagnostics, and regression tests for real platform quirks

Quick start

Install

Install the adapter and the Azure Functions peer dependency:

npm install -D @ktarmyshov/svelte-adapter-azure-swa @azure/functions

Configure SvelteKit

Use the adapter in svelte.config.js:

import adapter from '@ktarmyshov/svelte-adapter-azure-swa';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	preprocess: vitePreprocess(),
	kit: {
		adapter: adapter()
	}
};

export default config;

TypeScript setup

If your project uses TypeScript, include the adapter types in src/app.d.ts:

/// <reference types="svelte-adapter-azure-swa" />

Build

Run your normal SvelteKit production build:

npm run build

The adapter prepares the Azure Static Web Apps deployment artifacts as part of the build output.

Recommended Azure SWA deployment flow

Build first, then deploy

Recommended deployment flow:

  1. install dependencies
  2. run the app build yourself in CI
  3. prepare the generated API output for deployment as needed
  4. deploy the already-built output with the Azure Static Web Apps deploy action

Treat the Azure Static Web Apps GitHub Action as a deploy/upload step, not as the primary build system, unless you have a strong reason to do otherwise.

Why this is recommended

Building the project yourself before deploy is recommended because it is typically:

  • faster
  • more predictable
  • easier to control in CI
  • less exposed to slow or flaky Oryx build detection
  • less exposed to Azure/Oryx filesystem permission side effects

In practice, this also avoids cases where an Azure-managed build modifies directories in ways that interfere with later CI steps.

Example GitHub Actions flow

The repository CI workflow at ./.github/workflows/ci-swa.yml is the canonical example.

A typical flow is:

  1. build the app yourself
  2. if needed, install production dependencies into the generated or custom API directory
  3. deploy prebuilt output with:
    • skip_app_build: true
    • skip_api_build: true

A minimal deployment step looks like this:

- uses: Azure/static-web-apps-deploy@v1
  with:
    action: upload
    app_location: ./build/static
    api_location: ./build/server
    skip_app_build: true
    skip_api_build: true

If you use a custom apiDir, api_location should point to that directory instead.

Path mapping

For the default output layout, the important Azure SWA action inputs are typically:

| input | value | | ---------------- | ---------------- | | app_location | ./build/static | | api_location | ./build/server | | skip_app_build | true | | skip_api_build | true |

When deploying prebuilt output, output_location is typically not needed.

If your app lives in a subdirectory, adjust the paths accordingly.

What the adapter generates

Azure Static Web Apps deployment layout

The adapter prepares the Azure Static Web Apps deployment layout from the SvelteKit build output:

  • Azure Functions output for the server build
  • static content output for the frontend

With the default output layout, the generated files look like this:

build/
├── server/
│   ├── sk_render/
│   │   ├── entry.js
│   │   ├── index.js
│   │   └── ...
│   ├── host.json
│   ├── local.settings.json
│   └── package.json
└── static/
    ├── _app/
    ├── staticwebapp.config.json
    └── ...

Generated staticwebapp.config.json

The adapter generates staticwebapp.config.json with SWA-oriented defaults and routing behavior required for the deployment layout.

This includes safeguards around configuration areas that would otherwise break the generated SWA integration.

Generated API package manifest

For the default generated API output path, the adapter also prepares a deployment-oriented package.json.

When you configure additional server externals, those dependencies are automatically carried into the generated API manifest for the default API output flow.

Placeholder root index behavior

If the application root is not prerendered, the adapter writes the placeholder root index behavior needed by the generated SWA deployment layout.

Configuration options

apiDir

Controls where the generated Azure Functions output is written.

By default, the adapter writes the server output to build/server and also prepares the surrounding Azure Functions deployment files there.

import adapter from '@ktarmyshov/svelte-adapter-azure-swa';

export default {
	kit: {
		adapter: adapter({
			apiDir: 'custom/api'
		})
	}
};

When you do not override apiDir, the adapter prepares the default Azure Functions deployment layout for you, including:

  • host.json
  • local.settings.json
  • generated package.json
  • automatic inclusion of configured externals in that generated package.json

When you do override apiDir, the adapter writes only the generated sk_render function into your custom API directory. It does this to avoid overwriting an API layout you manage yourself.

That means a custom apiDir shifts more deployment responsibility to you. In particular, your custom API directory must already be a valid Azure Functions location.

A custom apiDir output looks like this:

custom/
└── api/
    └── sk_render/
        ├── entry.js
        ├── index.js
        └── ...

When using a custom apiDir, you are responsible for files such as host.json and package.json at the API root.

Your custom API package.json should use a main glob that includes both the generated sk_render/index.js entrypoint and any additional Azure Functions you deploy from that directory.

Example:

{
	"main": "**/index.js",
	"dependencies": {
		"@azure/functions": "^4"
	}
}

Also note that the adapter reserves the folder prefix sk_render and the function route prefix sk_render for the generated function.

staticDir

Controls where the generated static content output is written.

By default, the adapter writes static assets to build/static.

import adapter from '@ktarmyshov/svelte-adapter-azure-swa';

export default {
	kit: {
		adapter: adapter({
			staticDir: 'custom/static'
		})
	}
};

Override this when you need a different deploy layout or want to integrate the static output into an existing structure.

customStaticWebAppConfig

Allows extending the generated staticwebapp.config.json.

import adapter from '@ktarmyshov/svelte-adapter-azure-swa';

export default {
	kit: {
		adapter: adapter({
			customStaticWebAppConfig: {
				routes: [
					{
						route: '/login',
						allowedRoles: ['admin']
					}
				],
				globalHeaders: {
					'X-Content-Type-Options': 'nosniff'
				},
				platform: {
					apiRuntime: 'node:22'
				}
			}
		})
	}
};

The adapter intentionally guards configuration areas that are critical to the generated SWA integration. Attempting to override the catch-all route or navigationFallback will throw.

Custom SWA configuration can still affect how requests are handled, so test any changes carefully.

allowReservedSwaRoutes

Controls whether Azure SWA-reserved routes such as /api are allowed.

In production, Azure SWA routes /api and /api/* to the SWA API backend. SvelteKit routes beginning with /api can appear to work in development but return 404 in production because Azure never routes them to your SvelteKit app.

By default, the adapter throws at build time if it detects such routes.

import adapter from '@ktarmyshov/svelte-adapter-azure-swa';

export default {
	kit: {
		adapter: adapter({
			allowReservedSwaRoutes: true
		})
	}
};

Setting this option only suppresses the build-time check. It does not make Azure route /api requests to SvelteKit.

external

Marks selected server dependencies as external in the generated server bundle.

The adapter always includes these required externals:

  • fsevents
  • @azure/functions

You can add more externals like this:

import adapter from '@ktarmyshov/svelte-adapter-azure-swa';

export default {
	kit: {
		adapter: adapter({
			external: ['@sentry/sveltekit']
		})
	}
};

For the default generated API output path, configured externals are automatically added to the generated API package.json when those dependencies exist in your app's package.json.

For custom API layouts, you are responsible for making sure the deployed API directory has the production dependencies it needs.

emulate

Enables local Azure Static Web Apps platform emulation.

import adapter from '@ktarmyshov/svelte-adapter-azure-swa';

export default {
	kit: {
		adapter: adapter({
			emulate: {
				role: 'authenticated'
			}
		})
	}
};

This is useful when your application uses App.Platform and you want local development or tests to behave more like Azure SWA.

The emulation supports authenticated and anonymous flows and can provide a mock clientPrincipal, user, and Azure invocation context.

serverRolldown

Advanced customization hook for the server-side Rolldown pipeline.

Use this only when you need to adjust the generated server bundling behavior directly.

Diagnostics and test-oriented options

The adapter also exposes options such as:

  • debug
  • testWorkarounds
  • preserveAuthorization

These are useful for diagnostics, platform-probe behavior, and test flows, but they are not part of the main deployment path.

preserveAuthorization

Controls how the adapter handles the inbound Authorization request header before constructing the SvelteKit Request.

  • Default: false — the adapter strips the inbound Authorization header so SvelteKit endpoints, hooks, and SSR loads do not observe it.
  • true — the adapter forwards the inbound Authorization header byte-for-byte. Opt in only if you explicitly want the platform-supplied value.

The default-false behavior is based on the empirical observation (see issue #218) that real Azure Static Web Apps routinely injects or overwrites the inbound Authorization header on its managed Functions surface — across all adapter-supported HTTP methods and through both navigationFallback and explicit per-path rewrite routing, even when the client never sent an Authorization header. The local Azure SWA CLI emulator does not reproduce this behavior. Without the strip, every app deployed through the adapter is silently exposed to a platform-injected bearer token in request.headers.get('authorization'), typically interpreted by user code as the end-user's credential. Stripping by default closes that exposure.

preserveAuthorization: true is an escape hatch for users who intentionally want the raw platform Authorization header — for example, to introspect the Azure-injected token. Most applications should not enable it.

Bearer-auth behind SWA caveat. Applications that need a client-supplied bearer token to traverse Azure SWA cleanly should not rely on Authorization unless they fully understand their SWA routing and runtime behavior, regardless of this option. Because Azure SWA's behavior around Authorization is not under the adapter's control, the safest pattern is to use an app-specific custom header (e.g. x-app-authorization) for application bearer auth and treat Authorization as platform-owned.

Public-origin header normalization (Host, X-Forwarded-Host, X-Forwarded-Proto)

Azure Static Web Apps proxies requests to a managed Azure Functions backend. From the function's point of view the inbound request describes the internal hop — host is typically <something>.azurewebsites.net, and x-forwarded-host / x-forwarded-proto may be missing, stale, or client-spoofed — while the public URL is supplied separately via the x-ms-original-url header. The adapter already uses x-ms-original-url to construct Request.url and excludes that header from the downstream SvelteKit Request.headers.

To keep Request.url and the origin-identifying headers self-consistent, the adapter additionally normalizes three downstream headers from the same trusted x-ms-original-url value before constructing the SvelteKit Request. This is unconditional — there is no adapter option for it — and applies whenever x-ms-original-url is present and parses as a valid absolute URL.

| Downstream header | Source | | ------------------- | ---------------------------------------------------------------------- | | Host | originalUrl.host (includes the port when present) | | X-Forwarded-Host | originalUrl.host | | X-Forwarded-Proto | originalUrl.protocol with the trailing colon stripped (e.g. https) |

Trust model. x-ms-original-url is set by Azure SWA itself; the adapter treats it as the single trusted source for the public origin. Any inbound x-forwarded-host / x-forwarded-proto value — whether provided by an upstream proxy, replayed from a previous hop, or spoofed by a client — is overwritten by the values derived from x-ms-original-url. SvelteKit code reading request.headers.get('host'), x-forwarded-host, or x-forwarded-proto therefore sees the same public origin as event.url.

Fallback behavior. When x-ms-original-url is absent (e.g. local development outside the SWA flow) or does not parse as an absolute URL, the adapter does not touch host, x-forwarded-host, or x-forwarded-proto — those headers pass through with their inbound values exactly as before. No new error path is introduced by the normalization step.

This is a separate concern from preserveAuthorization — Authorization handling is governed by that option and is unaffected by origin-header normalization. Both behaviors compose: with the default options, an inbound request carrying Authorization and x-ms-original-url produces a downstream SvelteKit Request whose Authorization is stripped and whose host / x-forwarded-host / x-forwarded-proto describe the public origin.

Instrumentation, sourcemaps, and observability

Instrumentation support

The adapter supports the SvelteKit instrumentation contract.

If your app uses src/instrumentation.server.*, the instrumentation file is included in the generated Azure Functions output and wired into the server build flow.

This matters for observability and tooling that expects SvelteKit instrumentation support.

Sourcemaps

The adapter uses a sourcemap-friendly output pipeline.

Specifically:

  • the generated Azure Functions server output is rebundled with sourcemaps enabled
  • the generated client output is also rebundled for correct sourcemap behavior
  • standard repository layouts work out of the box

Sentry in monorepos

Sentry source mapping in monorepos requires rewriting source paths relative to the repository root.

For that case, the package exports sentryRewriteSourcesFactory.

This rewrite is typically not needed outside monorepo layouts.

Local development and diagnostics

Regular development

Normal SvelteKit development remains normal. This adapter is primarily about production build output and Azure deployment layout.

Azure SWA CLI

You can validate the generated output with the Azure Static Web Apps CLI.

A typical swa-cli.config.json looks like this:

{
	"configurations": {
		"app": {
			"outputLocation": "./build/static",
			"apiLocation": "./build/server",
			"host": "127.0.0.1"
		}
	}
}

Run your build first, then start the CLI.

npm run build
swa start

If you use a custom apiDir, point apiLocation to that directory instead.

Platform emulation

Platform emulation can be useful even without the SWA CLI when you want local behavior for App.Platform-dependent code.

It is especially helpful for:

  • authenticated vs anonymous behavior
  • application code that reads platform.user
  • application code that reads platform.clientPrincipal
  • local test scenarios that expect an Azure-like platform object

Backend coverage example

The repository CI workflow at ./.github/workflows/ci-swa.yml demonstrates backend coverage collection for the SWA CLI / generated Azure Functions flow.

In that setup:

  • coverage is collected in V8 form
  • the generated reports are converted to lcov in CI

If this needs more explanation later, it can become a dedicated advanced guide.

Azure-specific behavior notes

Local SWA CLI behavior is useful for validation, but it is not always identical to live Azure behavior.

This repository tracks some Azure-specific runtime quirks with diagnostics and regression tests so that changes in Azure behavior are easier to detect over time.

Compatibility

This adapter targets:

  • SvelteKit projects using @sveltejs/kit
  • Azure Functions Node programming model v4
  • Node 20 and Node 22

The package currently declares:

{
	"engines": {
		"node": ">=20 <21 || >=22 <23"
	}
}

If you override the generated SWA config, keep the Azure runtime configuration aligned with the Node version you actually deploy.

Migration / differences from upstream

This README documents this maintained fork/rework rather than the original adapter.

Compared with upstream, notable user-facing differences include:

  • a Rolldown-based output pipeline
  • improved sourcemap handling
  • instrumentation support in the generated Azure Functions build flow
  • local SWA platform emulation
  • Azure-focused diagnostics and compatibility handling

The main README narrative is intentionally deployment-first rather than fork-history-first.

Troubleshooting

/api route conflicts

If your SvelteKit routes begin with /api, they can work locally but fail in production because Azure SWA reserves that route prefix for the API backend.

By default, the adapter throws at build time to protect you from this.

Azure build path mistakes

Common deployment mistakes include:

  • pointing app_location at the wrong directory
  • pointing api_location at the wrong directory
  • forgetting to update deploy paths after overriding apiDir or staticDir

Oryx / Azure-managed build issues

If you let the Azure deploy action build the project for you, you may encounter:

  • slow build detection
  • environment-resolution failures
  • filesystem permission side effects during CI

This is why the recommended deployment flow is to build first and upload prebuilt output.

SWA CLI vs Azure cloud differences

Successful local SWA CLI validation does not guarantee identical behavior in the live Azure environment.

Treat the CLI as a valuable validation tool, not as a perfect simulation.

Prerendered routes and trailingSlash in SWA CLI

When testing a SvelteKit app through the Azure Static Web Apps CLI, prerendered routes and full-refresh route handling may fail unless SWA-CLI-specific route option handling is applied.

In the demo app, removing the root-level trailingSlash policy caused two different failures during local SWA CLI testing:

  • /sverdle on full refresh entered a redirect loop and failed with ERR_TOO_MANY_REDIRECTS
  • /sverdle/how-to-play and /sverdle/how-to-play/ returned 404, even though tests/demo/src/routes/sverdle/how-to-play/+page.ts explicitly sets export const prerender = true

Two practical fixes are available.

Option 1: preferred when trailingSlash is not overridden below the root layout

If your app does not override trailingSlash in lower-level layouts or pages, the simplest fix is to define an SWA-CLI-aware root-level trailingSlash policy.

Use a shared SWA CLI environment helper:

// tests/demo/src/lib/swa-env.ts
import { PUBLIC_SWA_CLI } from '$env/static/public';

export const isSwaCli = PUBLIC_SWA_CLI === 'true';

Then derive the root-level trailing slash policy from it:

// tests/demo/src/routes/+layout.ts
import { isSwaCli } from '$lib/swa-env';

export const trailingSlash = isSwaCli ? 'always' : 'never';

In the demo app, restoring this root-level trailingSlash policy fixed the failing SWA CLI route handling.

Relevant demo files:

  • tests/demo/src/lib/swa-env.ts
  • tests/demo/src/routes/+layout.ts

Option 2: explicit per-route prerender control

If you prefer not to use the root-level trailingSlash fix, or if trailingSlash is already overridden in lower-level routes, disable prerender explicitly on prerendered routes for SWA CLI mode:

import { isSwaCli } from '$lib/swa-env';

export const prerender = !isSwaCli;

This is the more explicit route-level fix. It requires updating each prerendered route that should not be prerendered under SWA CLI.

For example, the demo app includes a prerendered leaf route here:

  • tests/demo/src/routes/sverdle/how-to-play/+page.ts

A minimal SWA-CLI-aware version would look like this:

import { dev } from '$app/environment';
import { isSwaCli } from '$lib/swa-env';

// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement
export const csr = dev;

// prerender in normal environments, but disable it for SWA CLI local testing
export const prerender = !isSwaCli;

Which option to use

Use the root-level trailingSlash fix when all of the following are true:

  • you are testing through SWA CLI
  • you want the least invasive fix
  • trailingSlash is not overridden in lower-level routes

Use route-level prerender = !isSwaCli when:

  • you want explicit control over prerendered routes
  • or trailingSlash is already customized below the root layout
  • or you do not want SWA-CLI-specific trailing slash behavior to affect the whole app

If trailingSlash is overridden in lower-level routes or layouts, review those overrides carefully for SWA CLI testing, as behavior may become inconsistent.

Monorepo Sentry path rewriting

If your app lives in a monorepo, Sentry source path rewriting relative to the repository root is required.

Use sentryRewriteSourcesFactory for that case.

Empty form POSTs returning 415

Azure SWA / Azure Functions can drop content-type on empty form submissions.

That can make SvelteKit return 415 Unsupported Media Type for empty form POSTs.

This adapter includes an updated workaround for the currently observed Azure behavior, and the repository keeps diagnostics and regression tests around that path to help detect future platform changes.

Acknowledgements

  • credit to the original adapter project for the upstream foundation
  • thanks to @sukeshpabolu for the Node 22 / Rolldown migration and for being the first external contributor to the project