Jump to content

Zmienne Środowiskowe

Create T3 App korzysta z paczki ZodÔćŚ w celu walidacji twoich zmiennych ┼Ťrodowiskowych podczas runtimeÔÇÖu oraz budowania aplikacji. Do┼é─ůczane s─ů z tego powodu dodatkowe narz─Ödzia w pliku src/env.mjs.

env.mjs

TLDR; Je┼╝eli chcesz doda─ç now─ů zmienn─ů ┼Ťrodowiskow─ů, musisz doda─ç j─ů zar├│wno do pliku .env, jak i zdefiniowa─ç jej walidator w pliku src/env.mjs.

Plik ten podzielony jest na dwie cz─Ö┼Ťci - schemat zmiennych i wykorzystywanie obiektu process.env, jak i logika walidacji. Logika ta nie powinna by─ç zmieniana.

env.mjs
const server = z.object({
  NODE_ENV: z.enum(["development", "test", "production"]),
});

const client = z.object({
  // NEXT_PUBLIC_CLIENTVAR: z.string(),
});

const processEnv = {
  NODE_ENV: process.env.NODE_ENV,
  // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
};

Schemat Dla Serwera

Zdefiniuj tutaj zmienne ┼Ťrodowiskowe dla serwera.

Koniecznie nie prefixuj tutejszych kluczy NEXT_PUBLIC_, aby przypadkiem nie ujawni─ç ich do klienta.

Schemat Dla Klienta

Zdefiniuj tutaj zmienne ┼Ťrodowiskowe dla klienta.

Aby ujawni─ç zmienne dla klienta dodaj prefix NEXT_PUBLIC. Je┼╝eli tego nie zrobisz, walidacja nie zadzia┼éa, pomagaj─ůc ci w wykryciu niew┼éa┼Ťciwej konfiguracji.

Obiekt processEnv

Wykorzystaj destrukturyzacj─Ö obiektu process.env.

Potrzebny jest nam obiekt, kt├│ry parseÔÇÖowa─ç mo┼╝emy z naszymi schematami Zoda, a z powodu sposobu w jaki Next.js przetwarza zmienne ┼Ťrodowiskowe, nie mo┼╝esz destrukturyzowa─ç obiektu process.env tak jak zwyk┼éego obiektu - trzeba to zrobi─ç manualnie.

TypeScript zapewni poprawno┼Ť─ç destrukturyzacji obiektu i zapobiegnie sytuacji, w kt├│rej zapomnisz o jakim┼Ť kluczu.

// ÔŁî To nie zadzia┼éa, musimy r─Öcznie "rozbi─ç" `process.env`
const schema = z.object({
  NEXT_PUBLIC_WS_KEY: z.string(),
});

const validated = schema.parse(process.env);

Logika Walidacji

Dla zainteresowanego czytelnika:

Zaawansowane: Logika walidacji

W zale┼╝no┼Ťci od ┼Ťrodowiska (serwer lub klient) walidujemy albo oba schematy, albo tylko schemat klienta. Oznacza to, i┼╝ nawet je┼Ťli zmienne ┼Ťrodowiskowe serwera nie b─Öd─ů zdefiniowane, nie zostanie wyrzucony b┼é─ůd walidacji - mo┼╝emy wi─Öc mie─ç jeden punkt odniesienia do naszych zmiennych.

env.mjs
const isServer = typeof window === "undefined";

const merged = server.merge(client);
const parsed = isServer
  ? merged.safeParse(processEnv)  // <-- na serwerze, sprawd┼║ oba schematy
  : client.safeParse(processEnv); // <-- na kliencie, sprawd┼║ tylko zmienne klienta

if (parsed.success === false) {
  console.error(
    "ÔŁî Invalid environment variables:\n",
    ...formatErrors(parsed.error.format()),
  );
  throw new Error("Invalid environment variables");
}

Nast─Öpnie korzystamy z obiektu proxy, aby wyrzuca─ç b┼é─Ödy, je┼Ťli chcesz skorzysta─ç z serwerowych zmiennych ┼Ťrodowiskowych na kliencie.

env.mjs
// proxy pozwala na zmian─Ö gettera
export const env = new Proxy(parsed.data, {
  get(target, prop) {
    if (typeof prop !== "string") return undefined;
    // na kliencie pozwalamy jedynie na zmienne NEXT_PUBLIC_
    if (!isServer && !prop.startsWith("NEXT_PUBLIC_"))
      throw new Error(
        "ÔŁî Attempted to access serverside environment variable on the client",
      );
    return target[prop]; // <-- w przeciwnym razie, zwr├│─ç warto┼Ť─ç
  },
});

Korzystanie Ze Zmiennych Środowiskowych

Je┼╝eli chcesz skorzysta─ç ze swoich zmiennych ┼Ťrodowiskowych, mo┼╝esz zaimportowa─ç je z pliku env.mjs i skorzysta─ç z nich tak, jak normalnie by┼éoby to mo┼╝liwe. Je┼╝eli zaimportujesz obiekt ten na kliencie i spr├│bujesz skorzysta─ç ze zmiennych serwera, wyst─ůpi b┼é─ůd runtime.

pages/api/hello.ts
import { env } from "../../env.mjs";

// `env` jest w pełni typesafe i zapewnia autouzupełnianie
const dbUrl = env.DATABASE_URL;
pages/index.tsx
import { env } from "../env.mjs";

// ÔŁî Wyrzuci to b┼é─ůd runtime
const dbUrl = env.DATABASE_URL;

// Ôťů To jest ok
const wsKey = env.NEXT_PUBLIC_WS_KEY;

.env.example

Poniewa┼╝ plik .env nie jest wrzucany na system kontroli wersji, do┼é─ůczamy tak┼╝e plik .env.example, w kt├│rym - jesli chcesz - mo┼╝esz zawrze─ç kopi─Ö pliku .env z usuni─Ötymi secretami. Nie jest to wymagane, jednak polecamy trzyma─ç aktualn─ů kopi─Ö przyk┼éadowego pliku, aby u┼éatwi─ç potencjalnym kontrybutorom rozpocz─Öcie pracy w ich ┼Ťrodowisku.

Niekt├│re frameworki i narz─Ödzia do budowania, takie jak Next.js, zalecaj─ů przechowywanie sekretnych warto┼Ťci w pliku .env.local i commitowanie plik├│w .env do projektu. Nie jest to przez nas jednak rekomendowane, poniewa┼╝ mo┼╝e to ┼éatwo prowadzi─ç do przypadkowego ujawnienia tych warto┼Ťci. Polecamy natomiast przechowywanie sekretnych warto┼Ťci w pliku .env, trzymanie pliku tego w .gitignore i commitowanie jedynie plik├│w .env.example.

Dodawanie Zmiennych Środowiskowych

Aby upewni─ç si─Ö, ┼╝e tw├│j projekt nie zbuduje si─Ö bez wymaganych zmiennych ┼Ťrodowiskowych, b─Ödziesz musia┼é doda─ç now─ů zmienn─ů w dw├│ch miejscach:

­čôä .env: Wprowad┼║ swoj─ů zmienn─ů ┼Ťrod. tak, jak to zwykle robisz (np. KLUCZ=WARTO┼Ü─ć)

­čôä env.mjs: Dodaj odpowiadaj─ůc─ů jej logik─Ö walidacji definiuj─ůc schemat Zod, np. KLUCZ: z.string(). Nast─Öpnie wykorzystaj obiekt process.env w processEnv, np. KEY: process.env.KEY.

Opcjonalnie mo┼╝esz zaktualizowa─ç plik .env.example:

­čôä .env.example: Wprowad┼║ swoj─ů zmienn─ů ┼Ťrodowiskow─ů, upewnij si─Ö jednak ┼╝e nie nie posiada ona warto┼Ťci, kt├│ra jest sekretna, np. KLUCZ=WARTO┼Ü─ć lub KLUCZ=

Przykład

Chc─Ö doda─ç m├│j token do API Twittera jako zmienn─ů ┼Ťrodowiskow─ů po stronie serwera

  1. Dodaj zmienn─ů ┼Ťrod. do pliku .env:
TWITTER_API_TOKEN=1234567890
  1. Dodaj zmienn─ů ┼Ťrodowiskow─ů do pliku env.mjs:
export const server = z.object({
  // ...
  TWITTER_API_TOKEN: z.string(),
});

export const processEnv = {
  // ...
  TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN,
};
  1. opcjonalnie: Dodaj zmienn─ů ┼Ťrodowiskow─ů do .env.example. Usu┼ä jednak token.
TWITTER_API_TOKEN=

Recent Contributors To This Page