Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/devops-use-config-module.md
1---2title: Use ConfigModule for Environment Configuration3impact: LOW-MEDIUM4impactDescription: Proper configuration prevents deployment failures5tags: devops, configuration, environment, validation6---78## Use ConfigModule for Environment Configuration910Use `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety.1112**Incorrect (accessing process.env directly):**1314```typescript15// Access process.env directly16@Injectable()17export class DatabaseService {18constructor() {19// No validation, can fail at runtime20this.connection = new Pool({21host: process.env.DB_HOST,22port: parseInt(process.env.DB_PORT), // NaN if missing23password: process.env.DB_PASSWORD, // undefined if missing24});25}26}2728// Scattered env access29@Injectable()30export class EmailService {31sendEmail() {32// Different services access env differently33const apiKey = process.env.SENDGRID_API_KEY || 'default';34// Typos go unnoticed: process.env.SENDGRID_API_KY35}36}37```3839**Correct (use @nestjs/config with validation):**4041```typescript42// Setup validated configuration43import { ConfigModule, ConfigService, registerAs } from '@nestjs/config';44import * as Joi from 'joi';4546// config/database.config.ts47export const databaseConfig = registerAs('database', () => ({48host: process.env.DB_HOST,49port: parseInt(process.env.DB_PORT, 10),50username: process.env.DB_USERNAME,51password: process.env.DB_PASSWORD,52database: process.env.DB_NAME,53}));5455// config/app.config.ts56export const appConfig = registerAs('app', () => ({57port: parseInt(process.env.PORT, 10) || 3000,58environment: process.env.NODE_ENV || 'development',59apiPrefix: process.env.API_PREFIX || 'api',60}));6162// config/validation.schema.ts63export const validationSchema = Joi.object({64NODE_ENV: Joi.string()65.valid('development', 'production', 'test')66.default('development'),67PORT: Joi.number().default(3000),68DB_HOST: Joi.string().required(),69DB_PORT: Joi.number().default(5432),70DB_USERNAME: Joi.string().required(),71DB_PASSWORD: Joi.string().required(),72DB_NAME: Joi.string().required(),73JWT_SECRET: Joi.string().min(32).required(),74REDIS_URL: Joi.string().uri().required(),75});7677// app.module.ts78@Module({79imports: [80ConfigModule.forRoot({81isGlobal: true, // Available everywhere without importing82load: [databaseConfig, appConfig],83validationSchema,84validationOptions: {85abortEarly: true, // Stop on first error86allowUnknown: true, // Allow other env vars87},88}),89TypeOrmModule.forRootAsync({90inject: [ConfigService],91useFactory: (config: ConfigService) => ({92type: 'postgres',93host: config.get('database.host'),94port: config.get('database.port'),95username: config.get('database.username'),96password: config.get('database.password'),97database: config.get('database.database'),98autoLoadEntities: true,99}),100}),101],102})103export class AppModule {}104105// Type-safe configuration access106export interface AppConfig {107port: number;108environment: 'development' | 'production' | 'test';109apiPrefix: string;110}111112export interface DatabaseConfig {113host: string;114port: number;115username: string;116password: string;117database: string;118}119120// Type-safe access121@Injectable()122export class AppService {123constructor(private config: ConfigService) {}124125getPort(): number {126// Type-safe with generic127return this.config.get<number>('app.port');128}129130getDatabaseConfig(): DatabaseConfig {131return this.config.get<DatabaseConfig>('database');132}133}134135// Inject namespaced config directly136@Injectable()137export class DatabaseService {138constructor(139@Inject(databaseConfig.KEY)140private dbConfig: ConfigType<typeof databaseConfig>,141) {142// Full type inference!143const host = this.dbConfig.host; // string144const port = this.dbConfig.port; // number145}146}147148// Environment files support149ConfigModule.forRoot({150envFilePath: [151`.env.${process.env.NODE_ENV}.local`,152`.env.${process.env.NODE_ENV}`,153'.env.local',154'.env',155],156});157158// .env.development159// DB_HOST=localhost160// DB_PORT=5432161162// .env.production163// DB_HOST=prod-db.example.com164// DB_PORT=5432165```166167Reference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration)168