Bird
Raised Fist0
NestJSframework~15 mins

Custom configuration files in NestJS - Deep Dive

Choose your learning style10 modes available
Overview - Custom configuration files
What is it?
Custom configuration files in NestJS are user-defined files that store settings and options for your application. They allow you to organize environment-specific or feature-specific data separately from your code. This helps your app adapt to different environments like development, testing, or production without changing the code itself.
Why it matters
Without custom configuration files, you would hardcode settings directly in your app, making it hard to change behavior or deploy in different environments. This can cause bugs, security risks, and slow development. Custom config files let you keep your app flexible, secure, and easier to maintain.
Where it fits
Before learning custom configuration files, you should understand basic NestJS modules and environment variables. After this, you can explore advanced configuration management, validation, and dynamic configuration loading.
Mental Model
Core Idea
Custom configuration files let you separate your app’s settings from code so you can easily change behavior without rewriting code.
Think of it like...
It’s like having a recipe book where the ingredients list is separate from the cooking instructions. You can swap ingredients without changing how you cook.
┌─────────────────────────────┐
│       Application Code       │
│  (uses config values here)   │
└─────────────┬───────────────┘
              │
┌─────────────▼───────────────┐
│    Custom Configuration      │
│  (separate file with values) │
└─────────────────────────────┘
Build-Up - 6 Steps
1
FoundationWhat is a configuration file
🤔
Concept: Introduce the idea of storing settings outside code in files.
A configuration file is a simple file that holds settings your app needs, like database addresses or API keys. Instead of writing these directly in your code, you keep them in a separate file. This file can be JSON, YAML, or JavaScript exporting an object.
Result
You understand that configuration files hold app settings separately from code.
Knowing that settings can live outside code helps you keep your app flexible and easier to update.
2
FoundationUsing config files in NestJS basics
🤔
Concept: Learn how to load a simple config file in a NestJS app.
In NestJS, you can create a config file like config.ts that exports an object with settings. Then import this file in your modules or services to access those settings. For example, export const config = { port: 3000 }; and import { config } from './config';
Result
Your app can read settings from a separate file instead of hardcoding them.
Separating config from code is easy and improves app organization.
3
IntermediateIntegrating with @nestjs/config module
🤔Before reading on: do you think @nestjs/config automatically reads any file you create, or do you need to tell it which files to load? Commit to your answer.
Concept: Use NestJS’s official config module to manage custom config files with environment support.
NestJS provides @nestjs/config, a module that loads environment variables and custom config files. You register ConfigModule.forRoot({ load: [customConfig] }) where customConfig is a function returning your config object. This lets you organize config in multiple files and access them via ConfigService.
Result
Your app can load custom config files dynamically and access settings via ConfigService.
Using @nestjs/config standardizes config management and supports environment-based configs.
4
IntermediateCreating environment-specific config files
🤔Before reading on: do you think you should write all environment configs in one file or separate files per environment? Commit to your answer.
Concept: Organize config files by environment to switch settings easily between dev, test, and prod.
Create separate config files like config.dev.ts, config.prod.ts exporting config objects. Use a main config loader that picks which file to load based on NODE_ENV environment variable. This way, your app automatically uses the right settings for the current environment.
Result
Your app adapts config automatically depending on environment without code changes.
Environment-specific config files prevent mistakes and make deployments safer and easier.
5
AdvancedValidating configuration with Joi
🤔Before reading on: do you think config validation is optional or critical for production apps? Commit to your answer.
Concept: Add validation to config values to catch errors early and ensure app stability.
Use Joi schema validation with @nestjs/config by passing validationSchema option to ConfigModule.forRoot(). Define rules for required keys, types, and formats. If config is invalid, the app fails to start, preventing runtime errors.
Result
Your app only runs with valid config, reducing bugs and crashes.
Validating config protects your app from misconfiguration and hard-to-debug errors.
6
ExpertDynamic and asynchronous config loading
🤔Before reading on: do you think config files can only be static, or can they be generated or fetched at runtime? Commit to your answer.
Concept: Load config dynamically or asynchronously, for example from remote sources or computed values.
NestJS config supports async config loading by passing an async function to load option. This lets you fetch secrets from vaults, call APIs, or compute config values before app starts. It also supports merging multiple config sources.
Result
Your app can use dynamic, secure, and flexible config sources beyond static files.
Dynamic config loading enables advanced use cases like secret management and runtime adaptation.
Under the Hood
NestJS config module reads configuration by executing functions that return config objects. It merges environment variables and custom config files into a single config object stored in a ConfigService singleton. When you inject ConfigService, it provides access to these merged settings. Validation schemas run synchronously during app startup to ensure config correctness.
Why designed this way?
This design separates config loading from usage, supports multiple config sources, and enforces validation early. It avoids hardcoding, supports environment switching, and allows asynchronous fetching of config, which was difficult in older static config approaches.
┌───────────────┐
│ Environment   │
│ Variables     │
└──────┬────────┘
       │
┌──────▼────────┐
│ Custom Config │
│ Files/Funcs   │
└──────┬────────┘
       │
┌──────▼────────┐
│ ConfigModule  │
│ (loads &     │
│ validates)   │
└──────┬────────┘
       │
┌──────▼────────┐
│ ConfigService │
│ (provides    │
│ config to app)│
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think custom config files automatically reload when you change them during app runtime? Commit yes or no.
Common Belief:Custom configuration files automatically update the app when changed without restarting.
Tap to reveal reality
Reality:Config files are loaded once at app startup; changes require restarting the app to take effect.
Why it matters:Expecting live reload can cause confusion and bugs if config changes are not applied immediately.
Quick: Do you think environment variables and custom config files are the same thing? Commit yes or no.
Common Belief:Environment variables and custom config files are interchangeable and serve the same purpose.
Tap to reveal reality
Reality:Environment variables come from the OS environment and are strings, while custom config files are code or structured data you control. They complement each other but are not the same.
Why it matters:Mixing them up can lead to security risks or misconfiguration.
Quick: Do you think you must write all config in one big file for NestJS? Commit yes or no.
Common Belief:All configuration must be in a single file for NestJS to work properly.
Tap to reveal reality
Reality:NestJS supports splitting config into multiple files and merging them, improving organization and maintainability.
Why it matters:Believing in one big file leads to messy, hard-to-maintain configs.
Quick: Do you think config validation is optional and only for big projects? Commit yes or no.
Common Belief:Config validation is optional and only needed for very large or complex apps.
Tap to reveal reality
Reality:Validation is critical even for small apps to catch errors early and avoid runtime failures.
Why it matters:Skipping validation can cause subtle bugs that are hard to diagnose.
Expert Zone
1
Config functions can depend on environment variables and other config values, enabling computed settings.
2
Merging multiple config sources follows a priority order, which can cause unexpected overrides if not carefully managed.
3
Async config loading can delay app startup, so it should be optimized and used only when necessary.
When NOT to use
Custom configuration files are not ideal when you need real-time config updates without restarting; in such cases, use a centralized config service or feature flags. Also, avoid complex logic inside config files; keep them simple and use services for dynamic behavior.
Production Patterns
In production, teams use environment-specific config files combined with secret management tools. They validate config on startup and use async loaders to fetch secrets securely. ConfigService is injected everywhere to keep code clean and testable.
Connections
Environment Variables
Builds-on
Understanding environment variables helps grasp how custom config files complement and extend environment-based settings.
Dependency Injection
Same pattern
ConfigService uses dependency injection, a core NestJS pattern, to provide config values cleanly across the app.
Software Separation of Concerns
Builds-on
Custom config files embody separation of concerns by isolating configuration from business logic, a key software design principle.
Common Pitfalls
#1Hardcoding config values inside service files.
Wrong approach:export class AppService { private readonly port = 3000; getPort() { return this.port; } }
Correct approach:import { ConfigService } from '@nestjs/config'; export class AppService { constructor(private configService: ConfigService) {} getPort() { return this.configService.get('port'); } }
Root cause:Not understanding the benefit of separating config from code leads to inflexible and hard-to-maintain apps.
#2Not validating config, leading to runtime errors.
Wrong approach:ConfigModule.forRoot({ load: [customConfig] }); // no validationSchema
Correct approach:import * as Joi from 'joi'; ConfigModule.forRoot({ load: [customConfig], validationSchema: Joi.object({ port: Joi.number().required() }), });
Root cause:Underestimating the importance of config correctness causes bugs that appear only at runtime.
#3Trying to read config files dynamically at runtime without restart.
Wrong approach:const config = require('./config'); // expecting changes in config.js to reflect immediately without restart
Correct approach:Restart the app after changing config files to reload settings properly.
Root cause:Misunderstanding that config loading happens once at startup.
Key Takeaways
Custom configuration files keep your app’s settings separate from code, making it easier to change behavior without rewriting.
NestJS’s @nestjs/config module helps load, merge, and validate config from multiple sources including custom files and environment variables.
Organizing config by environment ensures your app uses the right settings for development, testing, and production automatically.
Validating config early prevents runtime errors and improves app stability.
Advanced usage includes async and dynamic config loading for secure and flexible applications.

Practice

(1/5)
1. What is the main purpose of using custom configuration files in a NestJS application?
easy
A. To write all logic inside configuration files
B. To keep app settings organized and separate from the main code
C. To replace the main app module
D. To store user data permanently

Solution

  1. Step 1: Understand configuration role

    Custom configuration files are used to keep settings like API keys or database info separate from the main code.
  2. Step 2: Identify the correct purpose

    This separation helps organize the app and makes it easier to change settings without touching code logic.
  3. Final Answer:

    To keep app settings organized and separate from the main code -> Option B
  4. Quick Check:

    Custom config files organize settings [OK]
Hint: Custom configs separate settings from code for clarity [OK]
Common Mistakes:
  • Thinking config files hold app logic
  • Confusing config files with database storage
  • Assuming config files replace main modules
2. Which syntax correctly creates a named custom configuration file using registerAs in NestJS?
easy
A. export const config = registerAs('app', () => ({ port: 3000 }));
B. registerAs('app', { port: 3000 });
C. export default registerAs('app', () => ({ port: 3000 }));
D. registerAs('app', () => port: 3000);

Solution

  1. Step 1: Understand registerAs usage

    The registerAs function takes a name and a function returning a config object.
  2. Step 2: Check syntax correctness

    export const config = registerAs('app', () => ({ port: 3000 })); correctly exports a constant assigned to registerAs with a function returning an object.
  3. Final Answer:

    export const config = registerAs('app', () => ({ port: 3000 })); -> Option A
  4. Quick Check:

    registerAs needs a name and a function returning an object [OK]
Hint: Use export const with registerAs and a function returning an object [OK]
Common Mistakes:
  • Omitting export or const keywords
  • Passing object directly instead of a function
  • Incorrect arrow function syntax
3. Given this custom config file:
export const databaseConfig = registerAs('database', () => ({ host: 'localhost', port: 5432 }));

And this module setup:
ConfigModule.forRoot({ load: [databaseConfig] })

What will configService.get('database.host') return?
medium
A. Error: Config not found
B. 'database.host'
C. undefined
D. 'localhost'

Solution

  1. Step 1: Understand config loading

    The databaseConfig is loaded into ConfigModule with load, making its keys accessible.
  2. Step 2: Access nested config value

    Using configService.get('database.host') accesses the 'host' key inside the 'database' config, which is 'localhost'.
  3. Final Answer:

    'localhost' -> Option D
  4. Quick Check:

    Nested config keys return their values [OK]
Hint: Use dot notation to get nested config values [OK]
Common Mistakes:
  • Expecting the whole object instead of a value
  • Using wrong key names
  • Not loading config before accessing
4. What is wrong with this custom config file code?
export const appConfig = registerAs('app', () => { port: 3000 });
medium
A. The config name 'app' is invalid
B. Missing import of registerAs
C. The arrow function does not return the object properly
D. The port value must be a string

Solution

  1. Step 1: Check arrow function return

    The arrow function uses curly braces without a return statement, so it returns undefined instead of the object.
  2. Step 2: Correct syntax for returning object

    To return an object directly, wrap it in parentheses: () => ({ port: 3000 }).
  3. Final Answer:

    The arrow function does not return the object properly -> Option C
  4. Quick Check:

    Arrow functions need parentheses to return objects directly [OK]
Hint: Wrap object in parentheses to return from arrow function [OK]
Common Mistakes:
  • Using curly braces without return in arrow functions
  • Assuming config name must be special
  • Thinking port must be string
5. You want to create two custom config files, appConfig and dbConfig, and load both in your NestJS app. Which setup correctly loads and accesses the dbConfig.username value?
hard
A. Load both configs in ConfigModule.forRoot({ load: [appConfig, dbConfig] }) and get with configService.get('dbConfig.username')
B. Load only appConfig and get dbConfig.username directly
C. Load both configs but access dbConfig.username with configService.get('username')
D. Load configs separately in different modules without forRoot

Solution

  1. Step 1: Load multiple configs together

    Use ConfigModule.forRoot({ load: [appConfig, dbConfig] }) to load both custom configs at app start.
  2. Step 2: Access nested config by namespace

    Access dbConfig.username using configService.get('dbConfig.username') because configs are namespaced by their registration name.
  3. Final Answer:

    Load both configs in ConfigModule.forRoot({ load: [appConfig, dbConfig] }) and get with configService.get('dbConfig.username') -> Option A
  4. Quick Check:

    Load all configs and use namespaced keys to access [OK]
Hint: Load all configs in forRoot and use dot keys to access [OK]
Common Mistakes:
  • Loading only one config but accessing another
  • Accessing keys without namespace prefix
  • Trying to load configs outside forRoot
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard