Пошук уроків, статей та іншого контенту
Навчитеся отримувати типізовані значення конфігурації через ConfigService у модулях і сервісах.
ConfigServiceКонфігурація застосунку часто залежить від середовища, у якому він запускається:
порт HTTP-сервера;
назва застосунку;
адреса бази даних;
секретні ключі;
режими development або production.
Такі значення зручно зберігати у змінних середовища, наприклад у файлі .env, а отримувати в коді через ConfigService.
Пакет @nestjs/config надає:
ConfigModule — модуль для підключення конфігурації;
ConfigService — сервіс для читання значень конфігурації.
Якщо пакет ще не встановлено, додайте його до проєкту:
npm install @nestjs/configConfigModuleСпочатку потрібно імпортувати ConfigModule у кореневий модуль застосунку.
.envСтворіть у корені проєкту файл .env:
APP_NAME=Мій NestJS застосунок
PORT=3000
NODE_ENV=developmentЗначення зі змінних середовища спочатку читаються як рядки. Наприклад, навіть PORT=3000 буде прочитано як рядок "3000".
app.module.tsimport { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppService } from './app.service';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [AppService],
})
export class AppModule {}Метод forRoot() завантажує конфігурацію під час запуску застосунку.
Опція isGlobal: true робить ConfigService доступним в інших модулях без повторного додавання ConfigModule до їхнього imports.
Без isGlobal: true кожен модуль, якому потрібен ConfigService, повинен імпортувати ConfigModule самостійно.
ConfigService можна отримати через dependency injection у конструкторі сервісу.
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppService {
constructor(private readonly configService: ConfigService) {}
getApplicationInfo() {
const appName = this.configService.get<string>(
'APP_NAME',
'Невідома назва',
);
const nodeEnv = this.configService.get<string>(
'NODE_ENV',
'development',
);
const port = Number(
this.configService.get<string>('PORT', '3000'),
);
return {
appName,
nodeEnv,
port,
};
}
}Метод get() приймає ключ конфігурації та повертає його значення:
this.configService.get<string>('APP_NAME');У цьому прикладі:
'APP_NAME' — назва змінної середовища;
string — очікуваний тип значення;
результат — значення з .env або іншого джерела конфігурації.
Метод get() підтримує generic-параметр:
const appName = configService.get<string>('APP_NAME');
const nodeEnv = configService.get<string>('NODE_ENV');Це допомагає TypeScript визначати тип змінної у програмі.
Однак важливо розуміти: generic-параметр не перетворює значення під час виконання.
const port = configService.get<number>('PORT');Якщо у .env записано:
PORT=3000фактичне значення все одно буде рядком "3000", а не числом 3000.
Тому числові значення потрібно перетворювати явно:
const port = Number(
configService.get<string>('PORT', '3000'),
);Другий аргумент get() використовується, якщо змінна не задана:
const port = configService.get<string>('PORT', '3000');Якщо PORT відсутня, метод поверне рядок '3000'.
Це корисно для необов’язкових налаштувань:
const logLevel = configService.get<string>(
'LOG_LEVEL',
'info',
);Тепер застосунок використає рівень логування info, якщо LOG_LEVEL не вказано.
ConfigService у модуліConfigService можна використовувати не лише безпосередньо в сервісах, а й під час створення провайдерів у модулі.
Наприклад, створимо провайдер із налаштуваннями застосунку:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AppService } from './app.service';
export interface AppOptions {
name: string;
port: number;
}
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [
AppService,
{
provide: 'APP_OPTIONS',
inject: [ConfigService],
useFactory: (configService: ConfigService): AppOptions => {
const name = configService.get<string>(
'APP_NAME',
'NestJS застосунок',
);
const port = Number(
configService.get<string>('PORT', '3000'),
);
return {
name,
port,
};
},
},
],
exports: ['APP_OPTIONS'],
})
export class AppModule {}Тут NestJS:
створює ConfigService;
передає його у функцію useFactory;
отримує з конфігурації назву та порт;
створює провайдер із токеном 'APP_OPTIONS'.
Інший сервіс може отримати цей провайдер через ін’єкцію:
import { Inject, Injectable } from '@nestjs/common';
import { AppOptions } from './app.module';
@Injectable()
export class AppInfoService {
constructor(
@Inject('APP_OPTIONS')
private readonly options: AppOptions,
) {}
getInfo() {
return this.options;
}
}Такий підхід зручний, коли модулю потрібно один раз підготувати конфігурацію для кількох провайдерів.
Нижче наведено мінімальний приклад, у якому:
ConfigModule завантажує .env;
AppService читає типізовані значення;
порт перетворюється з рядка на число;
main.ts використовує конфігурацію для запуску сервера.
.envAPP_NAME=Навчальний застосунок
PORT=3000
NODE_ENV=developmentapp.module.tsimport { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppService } from './app.service';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
providers: [AppService],
exports: [AppService],
})
export class AppModule {}app.service.tsimport { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppService {
constructor(private readonly configService: ConfigService) {}
getAppName(): string {
return this.configService.get<string>(
'APP_NAME',
'NestJS застосунок',
);
}
getEnvironment(): string {
return this.configService.get<string>(
'NODE_ENV',
'development',
);
}
getPort(): number {
const port = Number(
this.configService.get<string>('PORT', '3000'),
);
if (Number.isNaN(port)) {
throw new Error('Значення PORT має бути числом');
}
return port;
}
}main.tsimport { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppService } from './app.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const appService = app.get(AppService);
const port = appService.getPort();
await app.listen(port);
console.log(
`${appService.getAppName()} запущено в режимі ${appService.getEnvironment()}`,
);
console.log(`Порт: ${port}`);
}
bootstrap();Після запуску:
npm run start:devзастосунок прочитає значення з .env і запуститься на порту 3000.
ConfigModule у звичайному модуліЯкщо ConfigModule не оголошено глобальним, його потрібно імпортувати в кожному модулі, де використовується ConfigService.
app.module.tsimport { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { UsersModule } from './users/users.module';
@Module({
imports: [
ConfigModule.forRoot(),
UsersModule,
],
})
export class AppModule {}users.module.tsimport { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { UsersService } from './users.service';
@Module({
imports: [ConfigModule],
providers: [UsersService],
})
export class UsersModule {}users.service.tsimport { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class UsersService {
constructor(private readonly configService: ConfigService) {}
getUsersEndpoint(): string {
const apiUrl = this.configService.get<string>(
'USERS_API_URL',
'http://localhost:3001',
);
return `${apiUrl}/users`;
}
}Якщо в ConfigModule.forRoot() встановлено isGlobal: true, імпортувати ConfigModule у UsersModule окремо не потрібно.
Під час роботи з ConfigService варто пам’ятати:
підключайте ConfigModule.forRoot() один раз у кореневому модулі;
використовуйте isGlobal: true, якщо конфігурація потрібна в багатьох модулях;
отримуйте ConfigService через конструктор;
зазначайте очікуваний тип через get<T>();
перетворюйте рядкові значення на number явно;
задавайте значення за замовчуванням для необов’язкових параметрів;
не зберігайте секретні значення безпосередньо у вихідному коді.
get<number>() без перетворенняНеправильно:
const port = configService.get<number>('PORT');Так generic-параметр лише повідомляє TypeScript про очікуваний тип, але не перетворює рядок на число.
Правильно:
const port = Number(
configService.get<string>('PORT', '3000'),
);ConfigModuleЯкщо ConfigModule не підключено, NestJS не зможе створити ConfigService.
Переконайтеся, що в кореневому модулі є:
ConfigModule.forRoot({
isGlobal: true,
})Ключ у коді має точно відповідати назві змінної:
configService.get<string>('APP_NAME');і:
APP_NAME=Мій застосунокПомилка в одному символі призведе до undefined або до використання значення за замовчуванням.
Якщо параметр може бути відсутнім, передбачте запасне значення:
const environment = configService.get<string>(
'NODE_ENV',
'development',
);ConfigModule підключає конфігурацію до NestJS-застосунку.
ConfigService отримується через dependency injection.
Значення читаються методом get<T>().
Generic-параметр типізує значення для TypeScript, але не виконує runtime-перетворення.
Значення з .env зазвичай є рядками.
Числа потрібно перетворювати за допомогою Number() або іншого явного перетворення.
isGlobal: true дає змогу використовувати ConfigService у різних модулях без повторного імпорту ConfigModule.
Через useFactory можна створювати типізовані провайдери конфігурації на рівні модуля.