今天來介紹 SvelteKit 怎麼使用環境變數(environment variables),所謂環境變數是指我們程式運行所需要的某些設定、參數或敏感資料等等東西存放在程式碼外,方便讓這些變數會隨著環境變化。
SvelteKit 的環境變數共有四種分別是 dynamic/private
、dynamic/public
、static/private
、static/public
,基本上只要記得一個大原則是「client-side 只能使用 public
的環境變數」而 server-side 是所有都能使用。
因為 SvelteKit 是使用 vite 進行打包,所以定義環境變數以及注入環境變數上其實是差不多的,就直接使用 .env
檔案即可,如果有多個 .env
檔案可以使用 --mode
來選擇注入不一樣的 .env
檔。
# in .env
API_KEY=apikey
PUBLIC_API_URL=https://api.example.com
在 .env
檔的所有變數都可以從 static
取得,接下來在根據變數名稱來決定他是 public
或是 private
。預設情況下如果是 PUBLIC_
開頭的值都會被視為 public
,反之則全部都算是 private
。
// in day27/+page.server.ts
import { API_KEY } from '$env/static/private';
import { PUBLIC_API_URL } from '$env/static/public';
import type { ServerLoad } from '@sveltejs/kit';
export const load: ServerLoad = () => {
console.log(API_KEY, PUBLIC_API_URL);
};
會發現 terminal 上的輸出跟我們 .env
的一致
<!-- in day27/+page.svelte -->
<script lang="ts">
import { API_KEY } from '$env/static/private';
import { PUBLIC_API_URL } from '$env/static/public';
console.log(`API_KEY=${API_KEY}`);
console.log(`PUBLIC_API_URL=${PUBLIC_API_URL}`);
</script>
然後在 +page.svelte (client-side)使用 $env/static/private
就會看到以下錯誤
然後當我把 $env/static/private
刪掉後就能正常運行了。
那該如何新增 $env/dynamic/(public|private)
呢?基本上就跟我們新增 process.env
一樣直接在指令前新增就好
PUBLIC_API_URL_2=https://api.v2.example.com API_KEY_2=apikey2 pnpm run dev
使用上只要直接 import { env } from '$env/dynamic/private'
然後 SvelteKit 會在 build 的時候幫我們產生 type
// in day27/+page.server.ts
import { env } from '$env/dynamic/private';
import { API_KEY } from '$env/static/private';
import { PUBLIC_API_URL } from '$env/static/public';
import type { ServerLoad } from '@sveltejs/kit';
export const load: ServerLoad = () => {
console.log(`API_KEY=${API_KEY}`);
console.log(`PUBLIC_API_URL=${PUBLIC_API_URL}`);
console.log(`[$env/dynamic/private]API_KEY_2=${env.API_KE}`);
console.log(`[$env/dynamic/private]PUBLIC_API_URL_2=${env.PUBLIC_API_URL_2}`);
};
我們看 terminal 的輸出可以看到 PUBLIC_API_URL_2
是 undefined
,這是因為他是 PUBLIC_
開頭的變數,所以就不會被放在 $env/dynamic/private
裡了。
所以只要改用 $env/dynamic/public
即可看到在 CLI 上所輸入的PUBLIC_API_URL_2
的值了
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import { API_KEY } from '$env/static/private';
import { PUBLIC_API_URL } from '$env/static/public';
import type { ServerLoad } from '@sveltejs/kit';
export const load: ServerLoad = () => {
console.log(`API_KEY=${API_KEY}`);
console.log(`PUBLIC_API_URL=${PUBLIC_API_URL}`);
console.log(`[$env/dynamic/private]API_KEY_2=${env.API_KE}`);
console.log(`[$env/dynamic/private]PUBLIC_API_URL_2=${env.PUBLIC_API_URL_2}`);
console.log(`[$env/dynamic/public]PUBLIC_API_URL_2=${publicEnv.PUBLIC_API_URL_2}`);
};
那一樣如果我在 +page.svelte
使用 $env/dynamic/private
會跳出錯誤
<script lang="ts">
import { PUBLIC_API_URL } from '$env/static/public';
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
console.log(`PUBLIC_API_URL=${PUBLIC_API_URL}`);
console.log(`$env/dynamic/public=${JSON.stringify(env, null, 2)}`);
console.log(`$env/dynamic/private=${JSON.stringify(publicEnv, null, 2)}`);
</script>
<h1>Day 27</h1>
直到我把 import { env } from '$env/dynamic/private'
刪除
其實在剛剛的例子中我們會發現即使我們傳進 process.env
的變數依然會可以在 static
被找到,這是因為 SvelteKit 在 build time 時會自動幫我們 merge process.env
及 .env
然後產生 type declaration 。
如果變數名稱一樣會是
process.env
覆蓋掉.env
的值
所以其實換句話說只要我們能確保 prcoess.env
我們在每個指令/環境都有確實傳入我們該傳入的環境變數,我們依然可以把它當作 static
來 import
同理 .env
檔的環境變數也可以出現在 dynamic
中
那就產生了兩個疑問「我該選擇使用 prcoess.env
還是 .env
來新增環境變數」以及「dynamic
以及 static
該如何選擇」
以管理的方便程度來看我會推薦優先使用 .env
來定義環境變數,除非真的是在 server run 時才能確定的環境變數才會建議使用 process.env
如果可以的話一律推薦使用 static
,因為 dynamic
在 prerender 期間是不能使用的
雖然現在還沒介紹到 prerender ,但可以先想成 SSG 的頁面都不能使用 dynamic
就好。
簡而言之,我自己是覺得 SvelteKit 的環境變數 dynamic
與 static
在某些情況下是可以一致的,只是用語意在限制/提醒開發者區分出這兩者,所以我自己的習慣會是 .env
來的一律當作 static
, prcoess.env
一律當作 dynamic
。
我的原因是 .env
上有的一定會出現 $env/*
的 type declaration ,但出現在 $env/*
的type declaration 不一定會出現在 .env
,所以基本上為了方便檢查這件事情我都會建議使用 .env
,畢竟查閱 .env
檔通常會比看 script 還好閱讀吧。