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');
};
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",
//...
}
(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');
};
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
.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, 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');
};
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)
so we have to use 'VITE' prefix no matter what?