Пошук уроків, статей та іншого контенту
Розберете формат змінних середовища, файли .env і правила безпечного зберігання локальних налаштувань.
Змінна середовища — це значення, яке програма отримує з оточення операційної системи під час запуску.
Зазвичай через змінні середовища зберігають:
порт застосунку;
адресу бази даних;
логін і пароль;
секретні ключі;
режим роботи застосунку;
адреси зовнішніх сервісів.
Такий підхід дає змогу не змінювати код під час переходу між середовищами:
локальна розробка;
тестування;
production.
Наприклад, код може бути однаковим, але локально застосунок підключається до однієї бази даних, а на сервері — до іншої.
У Node.js змінні середовища доступні через process.env:
const port = process.env.PORT;
console.log(port);Значення, отримані через process.env, завжди мають тип string | undefined. Якщо змінної немає, результатом буде undefined.
const port = process.env.PORT ?? '3000';У цьому прикладі використовується порт 3000, якщо змінна PORT не задана.
.envФайл .env — це текстовий файл із парами ІМ’Я=ЗНАЧЕННЯ.
Приклад:
PORT=3000
NODE_ENV=development
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=postgres
DATABASE_PASSWORD=local-passwordОсновні правила формату:
одна змінна записується в одному рядку;
між назвою та значенням використовується знак =;
назви зазвичай пишуть великими літерами;
пробіли навколо = краще не використовувати;
значення з пробілами можна взяти в лапки;
коментарі починаються зі знака #.
# Порт HTTP-сервера
PORT=3000
# Значення з пробілом
APP_NAME="My Nest Application"Значення з .env не є числами або булевими значеннями автоматично:
PORT=3000
DEBUG=trueУ програмі обидва значення спочатку будуть рядками:
const port = Number(process.env.PORT);
const debug = process.env.DEBUG === 'true';Тому значення потрібно явно перетворювати до потрібного типу.
.env у NestJSNestJS не надає окремий об’єкт для змінних середовища. Для зручної роботи з конфігурацією використовується пакет @nestjs/config.
Встановіть його в проєкті:
npm install @nestjs/configДалі підключіть ConfigModule у головному модулі застосунку:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
})
export class AppModule {}Метод forRoot() завантажує змінні з файлу .env.
Опція isGlobal: true робить ConfigModule доступним у всіх модулях застосунку. Тому не потрібно імпортувати його повторно в кожен модуль.
За замовчуванням NestJS шукає файл .env у корені проєкту — там само, де зазвичай розташований package.json.
ConfigServiceПісля підключення ConfigModule можна використовувати ConfigService.
Наприклад, створимо сервіс, який читає порт застосунку:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppConfigService {
constructor(private readonly configService: ConfigService) {}
getPort(): number {
const portValue = this.configService.get<string>('PORT') ?? '3000';
const port = Number(portValue);
if (!Number.isInteger(port) || port <= 0) {
throw new Error('PORT має бути додатним цілим числом');
}
return port;
}
}Зареєструйте сервіс у модулі:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppConfigService } from './app-config.service';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [AppConfigService],
exports: [AppConfigService],
})
export class AppModule {}Тепер цей сервіс можна використати під час запуску застосунку:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppConfigService } from './app-config.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = app.get(AppConfigService);
await app.listen(config.getPort());
}
bootstrap();Повний мінімальний приклад може мати таку структуру:
project/
├── .env
├── package.json
└── src/
├── app.module.ts
├── app-config.service.ts
└── main.tsФайл .env:
PORT=3000Файл src/app.module.ts:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppConfigService } from './app-config.service';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [AppConfigService],
exports: [AppConfigService],
})
export class AppModule {}Файл src/app-config.service.ts:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppConfigService {
constructor(private readonly configService: ConfigService) {}
getPort(): number {
const value = this.configService.get<string>('PORT') ?? '3000';
const port = Number(value);
if (!Number.isInteger(port) || port <= 0) {
throw new Error('PORT має бути додатним цілим числом');
}
return port;
}
}Файл src/main.ts:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppConfigService } from './app-config.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = app.get(AppConfigService);
await app.listen(config.getPort());
}
bootstrap();Після запуску застосунок використовуватиме значення PORT із .env.
.env.exampleФайл .env часто містить паролі та інші секрети, тому його не варто додавати до Git. Але іншим розробникам потрібно розуміти, які змінні необхідні для запуску.
Для цього створюють файл .env.example:
PORT=3000
NODE_ENV=development
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=postgres
DATABASE_PASSWORD=У .env.example зберігають:
назви необхідних змінних;
приклади безпечних значень;
порожні значення для секретів.
Кожен розробник створює власний .env на основі цього файлу:
cp .env.example .envПісля цього локальні значення можна змінити у файлі .env.
.envФайл .env не повинен потрапляти до репозиторію, якщо він містить секрети.
Додайте його до .gitignore:
.env
.env.*
!.env.exampleЦе правило:
ігнорує .env;
ігнорує інші локальні варіанти .env;
залишає .env.example доступним для команди.
До секретів належать:
паролі;
токени;
ключі доступу до API;
секрети для підпису токенів;
рядки підключення до бази даних.
Не слід:
записувати секрети безпосередньо в код;
надсилати .env у чат або публічний репозиторій;
виводити паролі через console.log;
зберігати production-секрети в репозиторії.
Якщо секрет уже потрапив у Git, простого видалення файлу недостатньо: він міг залишитися в історії комітів. У такому випадку секрет потрібно замінити або відкликати.
Локальний файл .env зручний під час розробки. На сервері змінні середовища зазвичай задаються засобами платформи розгортання або операційної системи.
Код при цьому залишається тим самим:
const environment = process.env.NODE_ENV ?? 'development';Локально можна мати:
NODE_ENV=developmentА на сервері платформа задасть:
NODE_ENV=productionВажливо не плутати файл .env.example із файлом, який реально завантажується. .env.example — це лише шаблон. NestJS не використовує його замість .env, якщо це явно не налаштовано.
undefinedПеревірте:
чи існує файл .env;
чи лежить він у корені проєкту;
чи правильно написана назва змінної;
чи підключено ConfigModule.forRoot();
чи перезапущено застосунок після зміни .env.
Зміни у файлі .env зазвичай застосовуються лише після нового запуску процесу Node.js.
Змінні середовища завжди читаються як рядки:
const port = process.env.PORT;Якщо потрібне число, виконайте перетворення:
const port = Number(process.env.PORT);Такий код не працює очікуваним чином:
const isDebug = process.env.DEBUG === true;process.env.DEBUG — це рядок, тому потрібно порівнювати з рядком:
const isDebug = process.env.DEBUG === 'true';.env випадково додали до GitПеревірте .gitignore і стан репозиторію:
git statusЯкщо файл уже був доданий до індексу Git, додавання правила до .gitignore саме по собі не видалить його з індексу. Для локального видалення з індексу використовують:
git rm --cached .envЯкщо у файлі були справжні секрети, їх також потрібно замінити.
Не виводьте весь об’єкт process.env і не записуйте паролі в журнали:
console.log(process.env);Для перевірки краще вивести лише безпечний факт наявності значення:
console.log('DATABASE_PASSWORD задано:', Boolean(process.env.DATABASE_PASSWORD));Змінні середовища зберігають налаштування, які залежать від середовища запуску.
У Node.js вони доступні через process.env.
Файл .env містить пари ІМ’Я=ЗНАЧЕННЯ.
Усі значення з .env спочатку є рядками.
У NestJS для зручної роботи з конфігурацією використовується @nestjs/config.
ConfigModule.forRoot({ isGlobal: true }) завантажує .env і робить ConfigService доступним у застосунку.
Файл .env із секретами потрібно додати до .gitignore.
Файл .env.example використовують як безпечний шаблон конфігурації без реальних секретів.
Production-секрети не слід зберігати в коді або репозиторії.