Blog Post

Apps on Azure Blog
6 MIN READ

Deploy full-stack, server-side rendered SvelteKit applications to Azure Static Web Apps

thomasgauvinmsft's avatar
Mar 21, 2024

SvelteKit is a modern frontend framework based on Svelte, emphasizing reactivity and efficient compilation for creating efficient and performant web applications. With built-in features like server-side rendering and routing, SvelteKit can be used to build full-stack applications, leveraging server-based functionality such as server load functions and API routes. Best of all, SvelteKit can be hosted on Azure Static Web Apps (and there’s a free plan, so let’s jump in!). (Follow along with the complete application, available on GitHub, and the deployed application.)Architecture diagram of SvelteKit full-stack on Azure Static Web Apps

 

With the SvelteKit adapter for Azure Static Web Apps, SvelteKit can conveniently be hosted on Static Web Apps with server-side rendering and full-stack capabilities. Similar to other server-enabled frontend frameworks, SvelteKit will build your project into static and Functions assets to deploy to a single Azure Static Web Apps resource. Let’s dive into how we can build a full-stack, server-side rendered SvelteKit app and deploy it to Static Web Apps.

SvelteKit builds a static and a dynamic Functions bundle for deployment to a single Azure Static Web Apps resource

 

Prerequisites

 

This tutorial article requires that you have Node.js installed locally, an Azure subscription and a GitHub account. For this sample application, we’ll be using an existing Azure CosmosDB for NoSQL to demonstrate how we can access sensitive backend services such as databases or 3rd party APIs from SvelteKit's server handlers. The schema of the Azure CosmosDB for NoSQL is as follows:

 

SWAStore (Database)
	Items (Container)
		Item (Item)
			{	
				id: string
				title: string
				price: number
			}
	Sales (Container)
		Item (Item)
			{
				id: string
				date: string (date in ISO String format)
				items : [
					{
						id: string
						quantity: number
					}
				]
			}

 

 

Getting started

 

We start by creating a SvelteKit project, which will contain our Svelte pages as well as server handlers which will access our database and provide CRUD functionality for our application. This application will be a sample dashboard that allows us to managed sales for an e-commerce site, specifying items in our store and sales that occurred.

 

npm create svelte@latest sveltekit-swa-full-stack-app
cd sveltekit-swa-full-stack-app
npm install
npm install /cosmos
code .
npm run dev

 

We can now access our development server at http://localhost:5173/.  We can create a .env file within the root of our project, and set our environment variables to our CosmosDB secrets for easy access in our server handlers.

 

COSMOSDB_KEY="<ENTER COSMOSDB KEY HERE>"
COSMOSDB_ENDPOINT="<ENTER COSMOSDB URL HERE>"

 

 

Edit the SvelteKit application to provide CRUD access to our store items

 

In SvelteKit, we use load functions (GET) and actions (POST) in order to provide CRUD functionality for our application. We’ll start by creating  the /src/routes/items/+page.server.js file which will provide the load functions and actions for our /src/routes/items/+page.svelte items page.

 

export const prerender = false;

import { CosmosClient } from '@azure/cosmos';
import { env } from '$env/dynamic/private';
import { fail } from '@sveltejs/kit';

const client = new CosmosClient({
    endpoint: env.COSMOSDB_ENDPOINT,
    key: env.COSMOSDB_KEY
});

export async function load({ params }) {
    console.log('SvelteKit load function processed a request.');
    
    const database = client.database('SWAStore');
    const container = database.container('Items');
    
    const { resources: items } = await container.items.readAll().fetchAll();
    return {
        items: items
    };
}

export const actions = {
    delete: async ({ cookies, request }) => {
        console.log('SvelteKit delete action processed a request.');

        const data = await request.formData();
        const itemId = data.get('id');
        
        const database = client.database('SWAStore');
        const container = database.container('Items');

        try{
            await container.item(itemId, itemId).delete();
            return {
                success: true
            };
        }
        catch (error){
            return fail(500, 'Failed to delete item.')
        }
    }
}

 

The above snippet provides a load function to load all items from our database into the page, and a delete action which will be used to delete items in our database upon clicking the delete button. At the top of the file, notice that we are retrieving our database secrets from the environment variables and setting up our CosmosDB client. We are also setting prerender to false, such that this page is not executed at build time since we only want the CosmosDB to be accessed at runtime. Now that we have our server-side functions for getting and deleting items, we can create the Items page /src/routes/items/+page.svelte.

 

<script>
  export let data;
</script>

<h1>Items</h1>
<div>
  {#if data.items.length > 0}
    {#each data.items as item}
      <form method="POST">
        <div key={item.id}>
          <div>
            <div>#{item.id} - {item.title}</div>
            <div>${item.price}</div>
            <input hidden name="id" value={item.id} />
          </div>
          <div>
            <a href={`/items/edit/${item.id}`}>Edit</a>
            <button formaction="?/delete">Delete</button>
          </div>
        </div>
      </form>
    {/each}
  {:else}
    <p>No items found.</p>
  {/if}
</div>
<div>
  <a href="/items/create">Create New Item</a>
</div>

 

Inspecting this code snippet, we can observe that 'export let data;' indicates that the page will receive data from the +page.server.js file, such that the ‘data’ variable will be populated with the return value from the load function from the +page.server.js file. We can then use this data to render each item on the page. We also provide each item with a form that, upon click of the delete button, calls the delete action that we created in the +page.server.js file.

 

Screenshot of the SvelteKit items page with CRUD functionality

We can further iterate on this Items section, by adding the ability to edit existing items or create new items. This implementation is available in the source code that accompanies this article. Try out the application for yourself: ashy-stone-032d5bd03.4.azurestaticapps.net/items 

 

Configuring server-side rendering for SvelteKit

 

By default, SvelteKit renders our page on the server on a fresh page hit, and subsequent navigations are performed with a SvelteKit-specific Http request/RPC that provides SPA-like fluidity, similar to how Nuxt.js and Next.js perform server-side rendering. We can optionally configure certain pages to opt out of server-side rendering using the SvelteKit page options.

 

Deploy SvelteKit to Azure Static Web Apps

 

To deploy a SvelteKit project to Static Web Apps, we’ll make use of the svelte-adapter-azure-swa adapter by Geoff Rich. This will build our SvelteKit project into a static bundle (./build/static) and an Azure Functions bundle (./build/server) for deployment to Static Web Apps global static host and serverless managed Functions. Start by installing the dependency:

 

npm install -D svelte-adapter-azure-swa

 

Then, configure your svelte.config.js to make use of the adapter:

 

import azure from 'svelte-adapter-azure-swa';

export default {
    kit: {
        adapter: azure()
    }
};

 

We can now create a GitHub repository for our SvelteKit application. This is the easiest way to get started with creating an Azure Static Web App.

 

With our GitHub repository created, we can create a new Azure Static Web Apps resource from the portal. In the deployment details of the create step, we must select the SvelteKit build preset. This will create a GitHub Actions workflow file to build and deploy our application to your Static Web Apps resource with the proper configuration. (When selecting the SvelteKit build preset, it will automatically set the output location (./build/static) and API location (./build/server) as required for the static and dynamic functions bundle).

 

Finally, we set our database environment variables for our Azure Static Web Apps resource. From the Azure Static Web Apps resource in the Azure Portal, we navigate to Environment variables and set our COSMOSDB_KEY as well as your COSMOSDB_ENDPOINT. 

 

Conclusion

 

This article demonstrates how we can build a full-stack SvelteKit application using SvelteKit’s convenient loaders and actions to provide CRUD functionality, and how we can host SvelteKit applications on Azure using the svelte-adapter-azure-swa adapter which conveniently bundles our application into static and dynamic functions content for easy deployment. Better yet, we can further iterate on this full-stack application to use Static Web Apps’ built-in authorization and other features that make building frontend web applications most convenient.

 

Get started deploying full-stack web apps for free on Azure with Azure Static Web Apps!

Updated Mar 21, 2024
Version 3.0
No CommentsBe the first to comment