DEV Community

Cover image for Stages and Environment Variables in Svelte (SvelteKit)
Will
Will

Posted on • Edited on

Stages and Environment Variables in Svelte (SvelteKit)

While developing your Svelte application, you're likely to come across a scenario where you need to use different values depending on what stage (local, staging, production, etc.) the application is running on.

For example, let’s say that you have the following +page.server.ts file which uses a load function that fetches blog posts from a local server, and passes them onto a +page.svelte route page:

+page.server.ts

import { error, type Load } from '@sveltejs/kit';

export const load: Load = async () => {
    const res = await fetch(`http://localhost:1337/api/blog-posts`);

    const { data } = await res.json();

    if (res.ok) {
        return { posts: data };
    }

    throw error(404, 'Unable to fetch posts');
};
Enter fullscreen mode Exit fullscreen mode

Now, you've deployed your backend (API) and are ready to deploy your frontend (Svelte application) to production - only you can't yet, because the URL provided to fetch is pointing to localhost. We need that URL to fetch from our deployed API (let's pretend it's deployed to https://APP.herokuapp.com/api/blog-posts).

Obviously, we could replace the URL with our production one (https://APP.herokuapp.com/api/blog-posts), deploy it, and change it back to localhost (http://localhost:1337/api/blog-posts) whenever we're making local changes... but that would quickly become tedious.

How can we dynamically use different URLs depending on what stage the application is running on?

In more recent versions of SvelteKit, the build scripts used are the following:

"scripts": {
  "dev": "vite dev",
  "build": "vite build",
  //...
}
Enter fullscreen mode Exit fullscreen mode

(This didn't use to be the case, they used to be svelte-kit dev and svelte-kit build respectively)

This means that we could (keep reading for a better solution) use Vite's MODE environment variable which is automatically provided to us. By default, the dev server (dev command) runs in development mode and the build command runs in production mode. (You can read more about modes here).

You can access environment variables via Vite using import.meta.env, so if you want to reference the MODE, you can use const { MODE } = import.meta.env (or const MODE = import.meta.env.MODE if you're not a fan of destructuring like myself).

Let's go back to the example, we could therefore add a ternary on the MODE which gives us:

+page.server.ts

import { error, type Load } from '@sveltejs/kit';

const { MODE } = import.meta.env;
const LOCAL_URL = 'http://localhost:1337/api/blog-posts';
const PROD_URL = 'https://APP.herokuapp.com/api/blog-posts'; // Replace with whatever your deployed URL is

export const load: Load = async () => {
    const res = await fetch(MODE === 'development' ? LOCAL_URL : PROD_URL);

    const { data } = await res.json();

    if (res.ok) {
        return { blogs: data };
    }

    throw error(404, 'Unable to fetch blogs');
};
Enter fullscreen mode Exit fullscreen mode

Assuming your endpoint doesn't require an API key, the above should hit the correct endpoint and return the stage-specific data (🥳). You can verify this by running npm run build to run the application in "production" mode.

How can we simplify this with custom .env variables?

Vite uses the dotenv package to load any additional environment variables in your envDir (which is defaulted to the root directory):

.env (loaded in all cases)
.env.local (loaded in all cases, ignored by git)
.env.[mode] (only loaded in specified mode)
.env.[mode].local (only loaded in specified mode, ignored by git)

Another couple of things to note:

  • An env file for a specific mode (e.g. .env.production) will take higher priority than a generic one (e.g. .env).
  • Additional environment variables must have the VITE_ prefix.

Knowing that, we can create the following...

.env.development (file in the root directory for the "development" mode)

contents:

VITE_API_URL=http://localhost:1337/api/blog-posts
Enter fullscreen mode Exit fullscreen mode

.env.production (file in the root directory for the "production" mode)

contents:

VITE_API_URL=https://APP.herokuapp.com/api/blog-posts
Enter fullscreen mode Exit fullscreen mode

...and we can simplify our load function by removing the ternary and by just using the VITE_API_URL environment variable. SvelteKit provides us with $env/static/public, so instead of using import.meta.env we can use import { VITE_API_URL } from '$env/static/private';, which gives us:

+page.server.ts

import { error, type Load } from '@sveltejs/kit';

import { VITE_API_URL } from '$env/static/private';

export const load: Load = async () => {
    const res = await fetch(VITE_API_URL);

    const { data } = await res.json();

    if (res.ok) {
        return { blogs: data };
    }

    throw error(404, 'Unable to fetch blogs');
};
Enter fullscreen mode Exit fullscreen mode

That's it! 👨‍💻

If you need any further information on the above or anything else (eg. adding new environments such as staging) check out the Vite docs, the SvelteKit docs or the official Svelte discord.

Top comments (1)

Collapse
 
tombohub profile image
tombohub

so we have to use 'VITE' prefix no matter what?