Angular environment variables - build time czy runtime?

Radosław Krajewski 2 lipca 2026
Kod Angular pokazujący, jak wstrzykiwać i używać angular environment variables w serwisie ConfigService.

Spis treści

Przeniesienie aplikacji Angular z lokalnego komputera na testy, staging i produkcję często ujawnia problem z adresami API, flagami funkcji oraz ustawieniami logowania. Temat angular environment variables dotyczy właśnie bezpiecznego i przewidywalnego zarządzania taką konfiguracją. Pokażę, jak działają pliki środowiskowe, kiedy użyć konfiguracji budowania, a kiedy lepiej wczytać ustawienia dopiero po uruchomieniu aplikacji.

Najważniejsze zasady konfiguracji Angulara w różnych środowiskach

  • Pliki environment pozwalają podmieniać ustawienia podczas budowania aplikacji.
  • environment.ts w aktualnym układzie Angular CLI pełni rolę bazowej konfiguracji produkcyjnej.
  • Adres API nie jest sekretem, ponieważ trafia do kodu JavaScript dostępnego w przeglądarce.
  • Konfiguracja runtime sprawdza się wtedy, gdy ten sam build ma działać w wielu środowiskach.
  • Pliki .env nie są natywnym mechanizmem Angulara i wymagają dodatkowego narzędzia lub skryptu.

Co w Angularze oznacza konfiguracja środowiskowa

Aplikacja zwykle potrzebuje innych ustawień lokalnie, innych na środowisku testowym i jeszcze innych na produkcji. Najczęściej zmieniają się adres API, poziom logowania, przełączniki funkcji, identyfikator aplikacji analitycznej albo adres usługi uwierzytelniania.

Angular CLI rozwiązuje ten problem głównie przez nazwane konfiguracje budowania oraz podmianę plików. Podczas kompilacji narzędzie wybiera właściwy wariant, dzięki czemu kod aplikacji może korzystać z jednego importu, a wartości zostaną dobrane na podstawie użytej konfiguracji.

To ważne rozróżnienie. Nie chodzi o zmienną systemową odczytywaną przez przeglądarkę w czasie działania. W klasycznym podejściu wartość jest wpisywana do gotowego bundla podczas builda, dlatego zmiana konfiguracji po wdrożeniu wymaga ponownego zbudowania aplikacji.

Pliki environment i file replacements krok po kroku

W projekcie, który nie ma jeszcze katalogu środowiskowego, można wygenerować go poleceniem:

ng generate environments

Angular CLI utworzy katalog podobny do poniższego:

src/environments/
├── environment.ts
├── environment.development.ts
└── environment.staging.ts

W aktualnym układzie bazowy plik environment.ts zawiera domyślne ustawienia produkcyjne. Przykład konfiguracji może wyglądać tak:

export const environment = {
  production: true,
  apiUrl: 'https://api.example.pl',
  enableDebugLogs: false
};

Dla środowiska deweloperskiego przygotowuję osobny wariant:

export const environment = {
  production: false,
  apiUrl: 'http://localhost:5000',
  enableDebugLogs: true
};

W kodzie aplikacji importuję zawsze plik bazowy, a nie plik z nazwą konkretnego środowiska:

import { environment } from '../environments/environment';

fetch(`${environment.apiUrl}/products`);

O tym, który plik zostanie użyty, decyduje sekcja fileReplacements w pliku angular.json. Dla konfiguracji deweloperskiej wygląda ona następująco:

{
  "configurations": {
    "development": {
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.development.ts"
        }
      ]
    }
  }
}

Budowanie konkretnego wariantu uruchamiam poleceniem:

ng build --configuration development
ng build --configuration staging
ng build

Ostatnie polecenie korzysta z konfiguracji domyślnej. Z kolei ng serve zazwyczaj uruchamia wariant deweloperski. Częsty błąd polega na założeniu, że sama nazwa pliku wystarczy. Angular nie wybiera automatycznie każdego nowego pliku, dlatego nową konfigurację trzeba powiązać z buildem w angular.json.

Jak wybrać między build time i runtime

Pliki środowiskowe są wygodne, ale nie pasują do każdego modelu wdrożenia. Jeśli dla każdej wersji aplikacji tworzę osobny build, podmiana plików jest prosta i dobrze współgra z CI/CD. Jeśli jednak chcę zbudować obraz Dockera tylko raz i uruchamiać go w kilku środowiskach, lepsza będzie konfiguracja ładowana po starcie.

Metoda Kiedy używać Najważniejsze ograniczenie
Pliki environment Osobny build dla development, staging i production Zmiana wartości wymaga ponownego builda
Opcja define Pojedyncze wartości przekazywane do builda z pipeline Wymaga nowego buildera aplikacji i odpowiednich deklaracji TypeScript
Plik config.json ładowany runtime Jeden artefakt wdrażany w wielu środowiskach Aplikacja musi zaczekać na wczytanie konfiguracji

Nowszy builder aplikacji Angulara udostępnia również opcję define, która pozwala zastępować identyfikatory podczas budowania. Może być przydatna dla wartości takich jak numer commita albo host API przekazywany z pipeline'u:

ng build --define API_RETRY=3

Przy tym podejściu TypeScript powinien znać typ używanego identyfikatora, na przykład dzięki deklaracji declare const API_RETRY: number;. Ja traktuję tę technikę jako dobre rozwiązanie dla kilku prostych stałych, ale przy większej konfiguracji wolę czytelny obiekt środowiskowy.

Konfiguracja runtime dla jednego buildu

Jeśli aplikacja ma działać w kilku wdrożeniach bez przebudowywania, można trzymać ustawienia w publicznym pliku, na przykład assets/config.json:

{
  "apiUrl": "https://api.example.pl",
  "enableDebugLogs": false
}

Serwis konfiguracyjny pobiera plik przed uruchomieniem interfejsu. W aplikacji standalone można wykorzystać funkcję inicjalizującą, która wykona żądanie jeszcze przed wyrenderowaniem głównego widoku. Najważniejsza zasada brzmi nie uruchamiaj logiki zależnej od konfiguracji przed zakończeniem ładowania pliku.

W praktyce pipeline może podczas wdrożenia wygenerować zawartość config.json z parametrów Azure App Service, kontenera albo innego systemu CI/CD. Dzięki temu ten sam katalog z plikami statycznymi trafia na testy i produkcję, a różni się tylko zewnętrzna konfiguracja.

To podejście ma cenę. Dochodzi obsługa błędu pobierania, walidacja schematu i stan oczekiwania podczas startu. Przy małej aplikacji może to być niepotrzebne komplikowanie projektu. Przy wielu środowiskach oraz częstych zmianach adresów usług zwykle szybko się zwraca.

Dlaczego pliki environment nie służą do przechowywania sekretów

Najważniejsza rzecz, o której łatwo zapomnieć, jest prosta: wszystko, co trafi do aplikacji Angular, może zostać odczytane przez użytkownika. Pliki środowiskowe są kompilowane do kodu klienta, więc nie wolno umieszczać w nich haseł, prywatnych kluczy ani sekretów Azure.

Publiczny adres API, nazwa środowiska czy identyfikator klienta przeznaczony do użycia w przeglądarce nie muszą być tajne. Sekretem pozostaje natomiast klucz pozwalający ominąć autoryzację albo wykonać uprzywilejowaną operację.

W aplikacji opartej na Angularze tokeny i sekrety powinny pozostać po stronie backendu. Dla projektu .NET sensowny układ to przechowywanie poufnych wartości w konfiguracji API, Azure Key Vault albo bezpiecznych zmiennych pipeline'u. Frontend komunikuje się z backendem, ale nie otrzymuje jego poświadczeń.

Dobrym testem jest pytanie, czy zaakceptowałbym publikację danej wartości w kodzie strony. Jeśli odpowiedź brzmi „nie”, nie powinna znaleźć się ani w environment.ts, ani w config.json, ani w parametrze define.

Najczęstsze błędy przy konfiguracji Angulara

Importowanie niewłaściwego pliku

Komponenty powinny importować bazową ścieżkę environment, a nie bezpośrednio environment.development. Bezpośredni import omija mechanizm podmiany i może sprawić, że produkcyjny build nadal będzie korzystał z ustawień lokalnych.

Oczekiwanie działania process.env w przeglądarce

Angular działa po stronie klienta, a przeglądarka nie ma bezpośredniego dostępu do zmiennych środowiskowych serwera. Biblioteki obsługujące pliki .env mogą wygenerować konfigurację podczas builda, ale nie tworzą magicznego połączenia z systemem operacyjnym po wdrożeniu.

Brak walidacji adresu API

Literówka w adresie backendu często wychodzi dopiero po wdrożeniu. Warto sprawdzać konfigurację w pipeline oraz wykonać prosty test health check dla każdego środowiska. Przy konfiguracji runtime dobrze walidować nie tylko obecność pola, ale też jego typ i poprawny format URL.

Różne nazwy pól w plikach

Jeżeli wariant lokalny ma pole apiUrl, a staging używa przypadkiem apiURL, problem może pojawić się dopiero w konkretnym buildzie. Pomaga wspólny interfejs TypeScript albo funkcja tworząca konfigurację, dzięki której każdy wariant musi dostarczyć ten sam zestaw wartości.

Przeczytaj również: API w .NET od podstaw - od endpointu do wdrożenia

Traktowanie flagi production jako mechanizmu bezpieczeństwa

Wartość production: true może sterować logowaniem, ale nie zastępuje autoryzacji, kontroli dostępu ani ochrony endpointów. Użytkownik może zmodyfikować kod klienta, dlatego każde zabezpieczenie musi być egzekwowane na serwerze.

Praktyczny model dla aplikacji Angular i Azure

Dla typowej aplikacji biznesowej korzystającej z API .NET przyjąłbym trzy konfiguracje: development, staging i production. W małym projekcie wystarczą pliki environment oraz osobny build w pipeline. To rozwiązanie jest czytelne, łatwe do debugowania i nie wymaga dodatkowej infrastruktury.

W większym systemie rozważyłbym plik runtime, szczególnie gdy jeden obraz kontenera ma być promowany między środowiskami. Sekrety przechowywałbym po stronie API lub w menedżerze sekretów, a frontendowi udostępniałbym wyłącznie publiczne ustawienia, takie jak adres usługi, nazwa regionu czy dozwolone przełączniki funkcji.

Przed wdrożeniem sprawdzam cztery rzeczy:

  • czy wybrana konfiguracja builda jest jawnie podana w pipeline,
  • czy aplikacja odwołuje się do właściwego pliku bazowego,
  • czy w bundlu nie ma sekretów ani danych testowych,
  • czy adres API odpowiada środowisku, na które wdrażam aplikację.

Taki prosty przegląd często daje więcej niż rozbudowane abstrakcje konfiguracyjne. Najlepsze rozwiązanie to nie to z największą liczbą plików, tylko takie, w którym wiadomo kiedy wartość jest wybierana, kto nią zarządza i czy może być publiczna.

Od environment.ts do świadomego modelu wdrożeń

Pliki środowiskowe Angulara są dobrym punktem startowym dla konfiguracji zależnej od buildu. Zapewniają prosty podział na development, staging i production, a mechanizm file replacements pozwala utrzymać jeden sposób importowania ustawień w całej aplikacji.

Gdy wymagania rosną, można przejść do wartości przekazywanych przez define albo konfiguracji ładowanej podczas uruchamiania. Niezależnie od wybranej metody trzeba pamiętać o jednej granicy: kod dostarczony do przeglądarki jest publiczny, więc prawdziwe sekrety zawsze powinny pozostać po stronie serwera.

FAQ - Najczęstsze pytania

Pliki environment sprawdzają się, gdy dla development, staging i production tworzysz osobne buildy. Konfiguracja runtime, na przykład publiczny plik config.json, jest lepsza, gdy jeden artefakt lub obraz Dockera ma działać w wielu środowiskach bez ponownego budowania.

Aplikacja powinna importować bazowy plik src/environments/environment.ts. Właściwy wariant, taki jak environment.development.ts, jest podstawiany przez sekcję fileReplacements w angular.json po uruchomieniu komendy z odpowiednią opcją --configuration.

Nie. Wartości z plików environment, parametru define i pliku config.json trafiają do aplikacji dostępnej w przeglądarce, więc użytkownik może je odczytać. Nie należy umieszczać tam haseł, prywatnych kluczy ani sekretów Azure; powinny pozostać po stronie backendu lub w menedżerze sekretów.

Typowe problemy to importowanie bezpośrednio pliku deweloperskiego, oczekiwanie działania process.env w przeglądarce, brak walidacji adresu API oraz różne nazwy pól w poszczególnych wariantach. Warto też pamiętać, że production: true nie zastępuje autoryzacji ani ochrony endpointów po stronie serwera.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

zmienne środowiskowe
angular cli
konfiguracja runtime
docker
ci/cd
Autor Radosław Krajewski
Radosław Krajewski
Nazywam się Radosław Krajewski i od 6 lat zgłębiam tajniki programowania .NET, chmury Azure oraz sztucznej inteligencji. Moja przygoda z tymi technologiami zaczęła się od fascynacji tym, jak złożone problemy można rozwiązywać za pomocą kodu i innowacyjnych narzędzi. Staram się przekazywać tę wiedzę w sposób zrozumiały, dzieląc się swoimi doświadczeniami i spostrzeżeniami na kursdotnet.pl. W moich artykułach skupiam się na praktycznych aspektach, porównuję różne rozwiązania i analizuję najnowsze trendy, aby dostarczyć Wam rzetelne i aktualne informacje, które pomogą Wam rozwijać się w tej dynamicznie zmieniającej się dziedzinie.

Udostępnij artykuł

Napisz komentarz