Stages and Environment Variables in Svelte (SvelteKit)
Tags: svelte, javascript, tutorial, webdev
Published: 2022-08-30 21:50:27

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`_
```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');
};
```

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](https://kit.svelte.dev/), the build scripts used are the following:

```
"scripts": {
  "dev": "vite dev",
  "build": "vite build",
  //...
}
```

(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](https://vitejs.dev/guide/env-and-mode.html#modes)).

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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) like myself).

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

_`+page.server.ts`_
```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');
};
```

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](https://github.com/motdotla/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
```

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

_contents:_
```
VITE_API_URL=https://APP.herokuapp.com/api/blog-posts
```

...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](https://kit.svelte.dev/docs/modules#$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`_
```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');
};
```

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](https://vitejs.dev/guide/), the [SvelteKit docs](https://kit.svelte.dev/docs/introduction) or the official [Svelte discord](svelte.div/chat).