|
| 1 | +Much has been written about environment variables in SvelteKit. Yet, I was often unsure which of the |
| 2 | +four options SvelteKit offers for environment variables is the right one. Until I realized that you |
| 3 | +have to put the four options into the context of your hosting environment to make sense. |
| 4 | + |
| 5 | +Let's first look at where in a SvelteKit app you might need environment variables: |
| 6 | + |
| 7 | +1. **On the server:** Some of your SvelteKit code will run on a server, be it in a serverless |
| 8 | + function on Vercel or on a Node.js server, possibly inside a Docker container. The server waits |
| 9 | + for requests, processes them, and returns a response. The code on the server is considered |
| 10 | + secure, which means that its code and the environment cannot be accessed or inspected. That's why |
| 11 | + you can store secrets on a server, either in environment variables or baked into the code that |
| 12 | + runs on the server. |
| 13 | + |
| 14 | +2. **In the JavaScript code that runs in the browser:** When the server sends a response, it adds |
| 15 | + client-side code that runs in the browser. This highly optimized and bundled code is what creates |
| 16 | + a great UX for the users of your SvelteKit app. The server can send pre-bundled code containing |
| 17 | + values from environment variables, or it can dynamically add the values of environment variables |
| 18 | + into the code that it sends to the browser. In any case, these values must not contain any |
| 19 | + secrets, because as much as you may try to obfuscate them, they are part of the code shipped to |
| 20 | + the browser and can be inspected. |
| 21 | + |
| 22 | +3. **During the build step:** When you build your SvelteKit app, the build runs in a safe |
| 23 | + environment. Either on your development machine (which you hopefully update regularly) or on a |
| 24 | + build server, for example Vercel or a service like Railway. During the build step, the values of |
| 25 | + environment variables can be bundled into the code. If a bundle is meant to run on the server |
| 26 | + only, it can normally contain secrets (with some exceptions). If a bundle is meant to be shipped |
| 27 | + to the browser, it must not contain any secrets. |
| 28 | + |
| 29 | +Therefore, SvelteKit makes a distinction between environment variables that are secrets, referred to |
| 30 | +as **private variables**, and those that are safe to ship to a browser, referred to as **public |
| 31 | +variables**. |
| 32 | + |
| 33 | +When you are dealing with private variables, you have to be mindful of how you deploy your SvelteKit |
| 34 | +app. If you deploy to Vercel, it's safe to embed private variables code that runs on Vercel. There |
| 35 | +is no way anyone can inspect the deployed serverless functions. |
| 36 | + |
| 37 | +But when you deploy with a Docker container, you have to be careful. If your container is private |
| 38 | +and can never be accessed by anyone, you can safely embed private variables into SvelteKit's server |
| 39 | +code. But if you publish a Docker container to a registry, anyone with access could reverse engineer |
| 40 | +the container and extract the secrets. |
| 41 | + |
| 42 | +With the distinction between public and private variables in mind, let's look at SvelteKit's four |
| 43 | +options for environment variables: |
| 44 | + |
| 45 | +- [`$env/static/public`](https://svelte.dev/docs/kit/$env-static-public): These environment |
| 46 | + variables start with a configurable prefix, `PUBLIC_`, to signal that they are safe to ship to the |
| 47 | + browser. They must exist at build time, not runtime. They are baked into the code during the build |
| 48 | + and cannot be changed at runtime. Environment variables in this category are suitable for pages |
| 49 | + that are prerendered. |
| 50 | + |
| 51 | + Example: The public tracking ID of an analytics tool, `PUBLIC_ANALYTICS_ID`. |
| 52 | + |
| 53 | +- [`$env/dynamic/public`](https://svelte.dev/docs/kit/$env-dynamic-public): These environment |
| 54 | + variables cannot be accessed at build time. Therefore, they are not suitable for pages that are |
| 55 | + prerendered. On the server, there must be an environment variable prefixed with `PUBLIC_` that can |
| 56 | + be modified by the server during runtime. You can send this variable to the browser using a load |
| 57 | + function. |
| 58 | + |
| 59 | + Example: A variable that triggers maintenance mode on the server, `PUBLIC_MAINTENANCE_MODE=false`. |
| 60 | + |
| 61 | +- [`$env/static/private`](https://svelte.dev/docs/kit/$env-static-private): Like its public |
| 62 | + counterpart, static private variables must exist during the build step and cannot be changed at |
| 63 | + runtime. SvelteKit will throw an error if you try to import these variables into code that ships |
| 64 | + to the browser. Any page that uses these variables can be prerendered. Do not use in combination |
| 65 | + with published Docker containers. |
| 66 | + |
| 67 | + Example: A private API key, `API_KEY`. |
| 68 | + |
| 69 | +- [`$env/dynamic/private`](https://svelte.dev/docs/kit/$env-dynamic-private): These environment |
| 70 | + variables cannot be accessed during the build and, like their public counterpart, are not suitable |
| 71 | + for pages that are prerendered. They can be changed at runtime. |
| 72 | + |
| 73 | + Example: A feature flag for a new feature, `CHAT_FEATURE_FLAG`, so DevOps can turn off the |
| 74 | + feature without redeploying if they detect a problem. |
| 75 | + |
| 76 | +When I build for Vercel, most of the time I use static private variables with |
| 77 | +`$env/static/private`. Occasionally, I need a static public variable using `$env/static/public`, for |
| 78 | +example, to make a `PUBLIC_ANALYTICS_ID` available client-side. |
| 79 | + |
| 80 | +If I ship a SvelteKit app as a Docker container, I should default to dynamic private variables with |
| 81 | +`$env/dynamic/private`. But this often interferes with prerendering. So, even with Docker, I prefer |
| 82 | +using static environment variables. In a published Docker container, you should skip prerendering |
| 83 | +if it means you risk exposing a secret. |
0 commit comments