Configuration Management
Externalized Configuration
// Bad: Hardcoded configuration
const dbUrl = 'mongodb://localhost:27017/mydb';
const apiKey = 'abc123';
// Good: Environment variables
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;Environment Variables
// .env file
DATABASE_URL=mongodb://localhost:27017/mydb
API_KEY=abc123
LOG_LEVEL=info
PORT=3000
// Load with dotenv
require('dotenv').config();
const config = {
database: {
url: process.env.DATABASE_URL,
poolSize: parseInt(process.env.DB_POOL_SIZE) || 10
},
api: {
key: process.env.API_KEY,
timeout: parseInt(process.env.API_TIMEOUT) || 5000
},
server: {
port: parseInt(process.env.PORT) || 3000,
logLevel: process.env.LOG_LEVEL || 'info'
}
};
module.exports = config;Configuration Service
// Consul configuration
const Consul = require('consul');
const consul = new Consul();
class ConfigService {
async get(key) {
const result = await consul.kv.get(key);
return result?.Value;
}
async set(key, value) {
await consul.kv.set(key, value);
}
async watch(key, callback) {
const watcher = consul.watch({
method: consul.kv.get,
options: { key }
});
watcher.on('change', (data) => {
callback(data?.Value);
});
return watcher;
}
}
// Usage
const configService = new ConfigService();
// Get configuration
const dbUrl = await configService.get('database/url');
// Watch for changes
configService.watch('database/url', (newUrl) => {
console.log('Database URL changed:', newUrl);
// Reconnect with new URL
});Kubernetes ConfigMap
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_URL: "mongodb://mongo:27017/mydb"
LOG_LEVEL: "info"
API_TIMEOUT: "5000"
---
# Deployment using ConfigMap
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
template:
spec:
containers:
- name: user-service
image: user-service:latest
envFrom:
- configMapRef:
name: app-configSecrets Management
# Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
DATABASE_PASSWORD: <base64-encoded>
API_KEY: <base64-encoded>
JWT_SECRET: <base64-encoded>
---
# Use in deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
template:
spec:
containers:
- name: user-service
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORDVault Integration
const vault = require('node-vault')({
endpoint: 'http://vault:8200',
token: process.env.VAULT_TOKEN
});
class SecretManager {
async getSecret(path) {
const result = await vault.read(path);
return result.data;
}
async setSecret(path, data) {
await vault.write(path, { data });
}
}
// Usage
const secretManager = new SecretManager();
const dbPassword = await secretManager.getSecret('secret/database/password');
const apiKey = await secretManager.getSecret('secret/api/key');Configuration Hierarchy
const config = require('config');
// config/default.json
{
"database": {
"host": "localhost",
"port": 27017
},
"server": {
"port": 3000
}
}
// config/production.json
{
"database": {
"host": "prod-db.example.com"
}
}
// Access configuration
const dbHost = config.get('database.host');
const serverPort = config.get('server.port');Dynamic Configuration
class DynamicConfig {
constructor() {
this.config = new Map();
this.watchers = new Map();
}
set(key, value) {
this.config.set(key, value);
// Notify watchers
const callbacks = this.watchers.get(key) || [];
callbacks.forEach(cb => cb(value));
}
get(key, defaultValue) {
return this.config.get(key) || defaultValue;
}
watch(key, callback) {
if (!this.watchers.has(key)) {
this.watchers.set(key, []);
}
this.watchers.get(key).push(callback);
}
async reload() {
// Reload from configuration service
const newConfig = await fetchConfigFromService();
for (const [key, value] of Object.entries(newConfig)) {
this.set(key, value);
}
}
}
const config = new DynamicConfig();
// Watch for changes
config.watch('featureFlags.newUI', (enabled) => {
console.log('New UI feature:', enabled ? 'enabled' : 'disabled');
});
// Reload periodically
setInterval(() => config.reload(), 60000);Feature Flags
class FeatureFlags {
constructor() {
this.flags = new Map();
}
set(name, enabled, percentage = 100) {
this.flags.set(name, { enabled, percentage });
}
isEnabled(name, userId = null) {
const flag = this.flags.get(name);
if (!flag || !flag.enabled) {
return false;
}
if (flag.percentage === 100) {
return true;
}
// Consistent hashing for gradual rollout
if (userId) {
const hash = this.hash(userId);
return (hash % 100) < flag.percentage;
}
return Math.random() * 100 < flag.percentage;
}
hash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash = hash & hash;
}
return Math.abs(hash);
}
}
const features = new FeatureFlags();
features.set('newCheckout', true, 25); // 25% rollout
// Usage
if (features.isEnabled('newCheckout', req.user.id)) {
return renderNewCheckout();
} else {
return renderOldCheckout();
}Validation
const Joi = require('joi');
const configSchema = Joi.object({
database: Joi.object({
url: Joi.string().uri().required(),
poolSize: Joi.number().min(1).max(100).default(10)
}).required(),
server: Joi.object({
port: Joi.number().port().required(),
logLevel: Joi.string().valid('debug', 'info', 'warn', 'error').default('info')
}).required()
});
// Validate configuration
const { error, value } = configSchema.validate(config);
if (error) {
throw new Error(`Invalid configuration: ${error.message}`);
}
module.exports = value;Best Practices
- Externalize configuration: No hardcoded values
- Use environment-specific configs: Dev, staging, prod
- Secure secrets: Use Vault or similar
- Validate configuration: Schema validation
- Support dynamic updates: Hot reload
- Version configuration: Track changes
- Document settings: Clear descriptions
Interview Tips
- Explain externalization: Environment variables
- Show hierarchy: Default, environment-specific
- Demonstrate secrets: Vault, Kubernetes Secrets
- Discuss dynamic config: Hot reload, watchers
- Mention feature flags: Gradual rollouts
- Show validation: Schema validation
Summary
Configuration management externalizes settings from code using environment variables, ConfigMaps, and configuration services. Secure secrets with Vault or Kubernetes Secrets. Support dynamic updates with watchers. Use feature flags for gradual rollouts. Validate configuration with schemas. Essential for managing microservices across environments.
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.