Strona główna / Artykuły

Waliduj zmienne środowiskowe Node.js za pomocą Zod i TypeScript

Przestań używać process.env we wszystkich plikach kodu Node.js. Jeden plik env.ts zweryfikowany za pomocą Zod wykrywa brakujące dane poufne podczas uruchamiania i dostarcza aplikacji skonfigurowany typami kod w TypeScript.

Wydawca: REACTAPP.TOP3 min czytania663 słów

Źródła: https://medium.com/@vetriselvan_11/validating-environment-variables-in-node-js-using-zod-typescript-guide-16f4b6bacaf4

Wyobraź sobie wdrożenie w piątkowe popołudnie. Budowa przebiega pomyślnie, serwer się uruchamia i wszyscy wracają do domu. Godzinę później użytkownicy zgłaszają, że logowanie nie działa. Okazuje się, że nikt nie ustawił wartości JWT_SECRET w środowisku produkcyjnym. Aplikacja uruchomiła się bez problemów i zaczęła działać nieprawidłowo dopiero wtedy, gdy pierwsza prośba dotarła do kodu wymagającego tej brakującej wartości.

Częste czytanie wartości z process.env w całym kodzie zwiększa prawdopodobieństwo takich incydentów. Poniżej pokazano, jak zastąpić tę praktykę walidowanym, typowanym modulem konfiguracyjnym przy użyciu Zod i TypeScript.

Zwykły podejście

Większość kodu w Node.js pobiera konfigurację dokładnie tam, gdzie jest jej potrzeba: numer portu w jednym pliku, adres bazy danych w innym.

// Some random controller file
const port = process.env.PORT || 3000;
mongoose.connect(process.env.MONGO_URI);

Jest to wygodne, ale ma trzy poważne problemy.

  1. Brak bezpieczeństwa typów. W TypeScript process.env.PORT ma typ string | undefined. Jeśli chcesz liczbę, każde miejsce wywołania musi ją ręcznie przetworzyć.
  2. Ciche błędy. Gdy MONGO_URI nie istnieje, proces nie zawiesza się podczas uruchamiania. Zawiesza się dopiero w momencie wykonania tej konkretnej linii, być może godziny po deploju, który wydawał się prawidłowy.
  3. Brak jedynego źródła prawdy. Początkujący nie ma listy zmiennych potrzebnych aplikacji. Ich jedyną opcją jest przeszukiwanie całego repozytorium w poszukiwaniu process.env.

Jeden plik kontrolny

Rozwiązaniem jest przestanie modyfikowania process.env w logice biznesowej. Jeden moduł, nazwijmy go env.ts, odczytuje dane środowiskowe raz podczas uruchamiania, weryfikuje je i eksportuje silnie typowany obiekt. Wszystkie inne elementy importują ten obiekt.

Zod, biblioteka do weryfikacji schematów oparta na TypeScript, zajmuje się tą weryfikacją.

Zainstaluj ją

npm install zod

Napisz schemat

src/config/env.ts i dokładnie opisz, jak powinno wyglądać środowisko.

import dotenv from 'dotenv';
import { z } from 'zod';

// Load variables from .env file (if in development)
dotenv.config();

// 1. Define the schema
const envSchema = z.object({
  PORT: z.string().transform(Number).default('3000'),
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  MONGO_URI: z.string(),
  JWT_SECRET: z.string(),
  JWT_EXPIRES_IN: z.string().default('1d'),
});

// 2. Parse the environment against the schema
const _env = envSchema.safeParse(process.env);

// 3. Fail fast if it's invalid
if (!_env.success) {
  console.error('Invalid environment variables:', _env.error.format());
  process.exit(1); // Crash the app immediately!
}

// 4. Export the typed, validated object
export const env = _env.data;

Co robi ten moduł

  • Deklaruje wymagania. MONGO_URI to obowiązkowy ciąg znaków. PORT jest podawany jako ciąg znaków, a funkcja .transform(Number) przekształca go, przy użyciu domyślnej wartości 3000. NODE_ENV musi być jedną z trzech dozwolonych wartości.
  • Szybko wykrywa błędy. Funkcja safeParse jest wykonywana natychmiast po zaimportowaniu pliku, dlatego należy ją umieścić na samym początku pliku wejściowego, niezależnie od tego, czy jest to app.ts, czy server.ts. Jeśli brakuje jakiejś obowiązkowej zmiennej lub ma ona niewłaściwą strukturę, wynik pokazuje błąd, komunikat o błędzie precyzyjnie wskazuje, co poszło nie tak, a process.exit(1) zatrzymuje proces przed obsługą żądań.
  • Eksportuje obiekt typowany. W przypadku sukcesu dane po przetworzeniu zawierają typy wywnioskowane przez Zod, więc env.PORT jest już liczbą.

Od tego momentu każdy plik może importować env i go używać.

Co otrzymujesz

  • Samodzielne uzupełnianie tekstu. Wpisanie env. w edytorze wyświetla wszystkie dostępne zmienne.
  • Brak rozproszonych rozwiązań awaryjnych. Wzory || "default_value" znikają z kontrolerów, ponieważ wartości domyślne znajdują się w schemacie.
  • Budowana dokumentacja. Schemat stanowi dokładny wykaz elementów niezbędnych do uruchomienia aplikacji, co jednocześnie odpowiada na pytania nowicjuszy dotyczące tego, co powinno znajdować się w pliku .env.

Brakująca tajna informacja teraz powoduje zatrzymanie procesu rozruchu w ciągu kilku sekund, z wyraźną wiadomością, zamiast pojawić się jako problem godzinę później w postaci niezadowolonego użytkownika.