Пошук уроків, статей та іншого контенту
Додасте схеми та правила валідації, щоб застосунок не запускався з відсутніми або некоректними параметрами.
Конфігурація застосунку зазвичай надходить із:
змінних середовища;
файлу .env;
параметрів контейнера або CI/CD;
секретів середовища виконання.
Усі значення зі змінних середовища надходять як рядки. Наприклад:
PORT=3000буде прочитано як рядок "3000", а не як число 3000.
Без валідації застосунок може:
запуститися без обов’язкового DATABASE_URL;
використати некоректний порт;
прийняти невідоме значення
виявити помилку лише під час першого запиту до певного сервісу.
Краще перевірити конфігурацію під час запуску. Якщо значення некоректні або відсутні, NestJS має завершити запуск із зрозумілим повідомленням про помилку.
Для валідації конфігурації використаємо @nestjs/config і бібліотеку joi:
npm install @nestjs/config joi
npm install --save-dev @types/node@nestjs/config інтегрує конфігурацію з NestJS, а joi описує схему та правила перевірки.
Створимо схему для таких змінних:
NODE_ENV — середовище запуску;
PORT — порт HTTP-сервера;
DATABASE_URL — адреса бази даних;
LOG_LEVEL — рівень журналювання.
Файл .env:
NODE_ENV=development
PORT=3000
DATABASE_URL=postgres://user:password@localhost:5432/app
LOG_LEVEL=infoСхему можна передати до ConfigModule.forRoot() через властивість validationSchema.
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import Joi from 'joi';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'test', 'production')
.default('development'),
PORT: Joi.number()
.integer()
.min(1)
.max(65535)
.default(3000),
DATABASE_URL: Joi.string()
.uri({
scheme: ['postgres', 'postgresql'],
})
.required(),
LOG_LEVEL: Joi.string()
.valid('error', 'warn', 'info', 'debug')
.default('info'),
}),
validationOptions: {
abortEarly: false,
allowUnknown: true,
},
}),
],
})
export class AppModule {}Для NODE_ENV дозволені лише три значення:
Joi.string().valid('development', 'test', 'production')Якщо змінна не задана, використовується значення за замовчуванням:
.default('development')Для PORT перевіряється, що значення:
є числом;
є цілим числом;
перебуває в діапазоні від 1 до 65535.
Joi.number()
.integer()
.min(1)
.max(65535)
.default(3000)DATABASE_URL є обов’язковим:
Joi.string().uri({
scheme: ['postgres', 'postgresql'],
}).required()Якщо змінна відсутня або не є коректним URI PostgreSQL, запуск завершиться помилкою.
Для LOG_LEVEL дозволені лише значення, перелічені через valid():
Joi.string()
.valid('error', 'warn', 'info', 'debug')
.default('info')abortEarly: falseЗа замовчуванням Joi може зупинитися після першої помилки. Значення false змушує показати всі помилки одразу.
Це зручніше під час запуску в CI або контейнері: не потрібно виправляти змінні середовища по одній.
allowUnknown: trueУ середовищі запуску можуть бути додаткові змінні, які не належать цьому застосунку. Наприклад, їх може додати операційна система, хостинг або CI-система.
allowUnknown: true дозволяє їм існувати, але схема все одно перевіряє всі змінні, які описані в ній.
Якщо застосунок має повністю забороняти невідомі змінні, можна використати allowUnknown: false. Однак тоді кожна додаткова змінна середовища може спричинити помилку запуску.
Після успішної валідації значення доступні через ConfigService.
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { ConfigService } from '@nestjs/config';
import { AppModule } from './app.module';
async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
const port = configService.get<number>('PORT', 3000);
const nodeEnv = configService.get<string>('NODE_ENV', 'development');
await app.listen(port);
console.log(`Застосунок запущено в середовищі ${nodeEnv} на порту ${port}`);
}
void bootstrap();Якщо PORT було прочитано через Joi як число, ConfigService поверне числове значення. У виклику get() можна вказати тип:
const port = configService.get<number>('PORT');Значення за замовчуванням у другому аргументі захищає від undefined на рівні TypeScript, але не замінює валідацію. Обов’язкові змінні мають бути описані через .required() у схемі.
Припустімо, у .env вказано:
NODE_ENV=staging
PORT=99999
DATABASE_URL=not-a-url
LOG_LEVEL=verboseПід час запуску NestJS не почне слухати HTTP-порт. Joi повідомить про всі некоректні значення:
NODE_ENV не входить до дозволеного списку;
PORT перевищує максимальне значення;
DATABASE_URL не є коректним URI;
LOG_LEVEL не входить до дозволеного списку.
Це важливо: помилка виникає на старті, а не в момент, коли застосунок уперше спробує підключитися до бази даних або записати журнал.
Замість validationSchema можна передати функцію через властивість validate. Це корисно, коли правила залежать одне від одного або потрібне додаткове перетворення значень.
// src/config/validate-config.ts
import Joi from 'joi';
export interface ValidatedConfig {
NODE_ENV: 'development' | 'test' | 'production';
PORT: number;
DATABASE_URL: string;
LOG_LEVEL: 'error' | 'warn' | 'info' | 'debug';
}
export function validateConfig(
config: Record<string, unknown>,
): ValidatedConfig {
const schema = Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'test', 'production')
.default('development'),
PORT: Joi.number()
.integer()
.min(1)
.max(65535)
.default(3000),
DATABASE_URL: Joi.string()
.uri({
scheme: ['postgres', 'postgresql'],
})
.required(),
LOG_LEVEL: Joi.string()
.valid('error', 'warn', 'info', 'debug')
.default('info'),
});
const { error, value } = schema.validate(config, {
abortEarly: false,
allowUnknown: true,
});
if (error) {
throw new Error(`Некоректна конфігурація:\n${error.message}`);
}
return value as ValidatedConfig;
}Підключення функції:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { validateConfig } from './config/validate-config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validate: validateConfig,
}),
],
})
export class AppModule {}У цьому варіанті функція:
отримує значення конфігурації;
перевіряє їх схемою Joi;
отримує значення за замовчуванням;
викидає помилку при невідповідності;
повертає перевірену конфігурацію.
Для простих схем зручніше використовувати validationSchema. Власна функція доречна, коли потрібна додаткова логіка, яку складно або незручно виразити схемою.
Запустіть застосунок із коректним .env:
npm run start:devПісля цього змініть порт:
PORT=0або видаліть обов’язкову змінну:
# DATABASE_URL відсутняПід час наступного запуску застосунок має завершитися з помилкою валідації.
Також можна перевірити неприпустиме середовище:
NODE_ENV=stagingОскільки staging не описано в схемі, NestJS не завершить запуск.
process.env у різних місцяхТакий підхід розподіляє правила по всьому застосунку:
const port = Number(process.env.PORT);
if (!port) {
throw new Error('Некоректний порт');
}Краще централізувати правила в ConfigModule. Тоді конфігурація перевіряється один раз під час запуску.
required()Правило типу:
DATABASE_URL: Joi.string()перевіряє лише тип, але дозволяє відсутнє значення. Для обов’язкової змінної потрібне:
DATABASE_URL: Joi.string().required()Змінні середовища є рядками, тому без схеми:
process.env.PORT === 3000завжди буде false, якщо PORT має значення "3000".
Схема Joi перетворює значення числових правил на числа, якщо валідація успішна.
Правило:
Joi.string()приймає будь-який непорожній рядок. Якщо список допустимих значень обмежений, використовуйте valid():
Joi.string().valid('error', 'warn', 'info', 'debug')allowUnknown: false без перевірки середовищаЯкщо встановити:
allowUnknown: falseJoi може відхилити додаткові змінні середовища, яких немає у схемі. Це допустимо для суворо контрольованого середовища, але може створити проблеми локально або в CI.
ConfigModule.forRoot() завантажує конфігурацію та інтегрує її з NestJS.
validationSchema дозволяє описати правила перевірки через Joi.
.required() робить змінну обов’язковою.
.default() задає значення за замовчуванням.
.valid() обмежує значення певним списком.
Для чисел потрібно враховувати, що змінні середовища спочатку є рядками.
abortEarly: false показує всі помилки конфігурації одразу.
За некоректної конфігурації застосунок не повинен запускатися.