From harness-claude
Manages NestJS environment config using ConfigModule.forRoot, ConfigService, and Joi validation. Loads .env files, enables fail-fast checks at startup, typed injection, and namespaced sections.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Manage environment config with ConfigModule.forRoot, ConfigService, and Joi schema validation
Provides expert Nest.js guidance on enterprise Node.js architecture, dependency injection, decorators, middleware, guards, interceptors, pipes, testing strategies, database integration, and authentication. Validates with typecheck, unit, integration, e2e tests.
Provides NestJS patterns for modules, controllers, providers, DTO validation, guards, interceptors, config, and production TypeScript backends. Useful for structuring APIs, adding validation, and database integrations.
Organizes NestJS applications using feature modules, controlled exports, dynamic configurations with forRoot/forFeature. Useful for structuring new features, sharing providers without circular dependencies, and building reusable modules.
Share bugs, ideas, or general feedback.
Manage environment config with ConfigModule.forRoot, ConfigService, and Joi schema validation
.env files and inject them into servicesprocess.env accessnpm install @nestjs/config
// app.module.ts
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // no need to import in each module
envFilePath: '.env', // default — loads .env
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid('development', 'production', 'test').required(),
DATABASE_URL: Joi.string().required(),
JWT_SECRET: Joi.string().min(32).required(),
PORT: Joi.number().default(3000),
}),
}),
],
})
export class AppModule {}
@Injectable()
export class DatabaseService {
constructor(private config: ConfigService) {
const url = this.config.get<string>('DATABASE_URL');
const port = this.config.get<number>('PORT', 3000); // with default
}
}
registerAs:// config/database.config.ts
export default registerAs('database', () => ({
url: process.env.DATABASE_URL,
maxConnections: parseInt(process.env.DB_MAX_CONNECTIONS ?? '10'),
}));
// app.module.ts
ConfigModule.forRoot({ load: [databaseConfig] })
// inject
constructor(
@InjectConfig('database') private dbConfig: ConfigType<typeof databaseConfig>
) {}
// or
this.config.get('database.url')
npm install joi):validationSchema: Joi.object({
DATABASE_URL: Joi.string().uri().required(),
JWT_SECRET: Joi.string().min(32).required(),
PORT: Joi.number().port().default(3000),
});
ConfigModule.forRoot({
envFilePath: ['.env.local', '.env'], // .env.local takes precedence
});
@nestjs/config is a thin wrapper around the dotenv package with NestJS-specific integration: DI injection, module-level encapsulation, and schema validation.
Fail-fast validation: When a validationSchema is provided, ConfigModule validates all environment variables at application bootstrap. If required variables are missing or invalid, the process exits with a descriptive error before any service initializes. This prevents runtime errors from missing config deep in the request path.
isGlobal: true: Makes ConfigModule available to all modules without explicit import. Since configuration is genuinely app-wide, this is almost always correct. Register it once in AppModule.
ConfigService.get<T>() typing: The generic type is a hint only — TypeScript cannot verify that process.env.PORT is actually a number. Use parseInt() or the namespaced registerAs() approach with proper type coercion.
Testing: In tests, use ConfigModule.forRoot({ envFilePath: '.env.test' }) or override with { provide: ConfigService, useValue: { get: jest.fn((key) => testValues[key]) } }.
Custom ConfigFactory pattern: For complex scenarios (secret manager, remote config), implement a ConfigFactory function and return the config object from an async source. Use ConfigModule.forRootAsync({ useFactory: ... }) with injected HTTP clients or secret manager SDKs.
https://docs.nestjs.com/techniques/configuration