Environment-Specific Configuration
This guide shows how to configure the response handler for different environments (development, staging, production).
Environment Configuration Structure
javascript
// config/environments.js
const baseConfig = {
logging: {
enabled: true,
includeRequestId: true,
includeTimestamp: true,
},
pagination: {
defaultPage: 1,
defaultLimit: 20,
maxLimit: 100,
},
};
const environments = {
development: {
...baseConfig,
logging: {
...baseConfig.logging,
level: 'debug',
format: 'simple',
destinations: [{ type: 'console' }],
},
environment: {
current: 'development',
showStackTrace: true,
exposeInternalErrors: true,
debugMode: true,
verboseLogging: true,
},
performance: {
enableCaching: false,
enableCompression: false,
enableEtag: false,
},
security: {
enableCors: true,
corsOptions: {
origin: ['http://localhost:3000', 'http://localhost:3001'],
credentials: true,
},
enableHelmet: false,
rateLimiting: {
windowMs: 60000,
maxRequests: 1000, // Very permissive for development
},
},
},
staging: {
...baseConfig,
logging: {
...baseConfig.logging,
level: 'info',
format: 'json',
destinations: [
{ type: 'console' },
{
type: 'file',
options: { filename: 'logs/staging.log' },
},
],
},
environment: {
current: 'staging',
showStackTrace: false,
exposeInternalErrors: false,
debugMode: false,
verboseLogging: false,
},
performance: {
enableCaching: true,
cacheTimeout: 300000, // 5 minutes
enableCompression: true,
compressionLevel: 6,
enableEtag: true,
},
security: {
enableCors: true,
corsOptions: {
origin: ['https://staging.yourapp.com'],
credentials: true,
},
enableHelmet: true,
rateLimiting: {
windowMs: 900000, // 15 minutes
maxRequests: 500,
},
},
},
production: {
...baseConfig,
logging: {
...baseConfig.logging,
level: 'error',
format: 'json',
destinations: [
{ type: 'console' },
{
type: 'file',
options: { filename: 'logs/production.log' },
},
{
type: 'http',
options: {
url: process.env.LOG_ENDPOINT,
headers: {
Authorization: `Bearer ${process.env.LOG_API_KEY}`,
},
},
},
],
maskSensitiveData: true,
},
environment: {
current: 'production',
showStackTrace: false,
exposeInternalErrors: false,
debugMode: false,
verboseLogging: false,
},
performance: {
enableCaching: true,
cacheTimeout: 600000, // 10 minutes
enableCompression: true,
compressionLevel: 9,
enableEtag: true,
maxResponseSize: 1048576, // 1MB
timeout: 30000, // 30 seconds
},
security: {
enableCors: true,
corsOptions: {
origin: [process.env.FRONTEND_URL],
credentials: true,
},
enableHelmet: true,
helmetOptions: {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
},
},
},
rateLimiting: {
windowMs: 900000, // 15 minutes
maxRequests: 100,
message: 'Too many requests from this IP',
},
sanitizeInput: true,
validateSchema: true,
},
},
};
export default environments;Environment Detection and Loading
javascript
// config/index.js
import environments from './environments.js';
function detectEnvironment() {
return process.env.NODE_ENV || 'development';
}
function loadConfig() {
const env = detectEnvironment();
const config = environments[env];
if (!config) {
throw new Error(`Configuration for environment '${env}' not found`);
}
// Override with environment variables if present
return mergeWithEnvVars(config);
}
function mergeWithEnvVars(config) {
const envOverrides = {};
// Logging overrides
if (process.env.LOG_LEVEL) {
envOverrides.logging = { ...config.logging, level: process.env.LOG_LEVEL };
}
// Database URL override
if (process.env.DATABASE_URL) {
envOverrides.database = { url: process.env.DATABASE_URL };
}
// Redis URL override
if (process.env.REDIS_URL) {
envOverrides.cache = { url: process.env.REDIS_URL };
}
// CORS origin override
if (process.env.CORS_ORIGIN) {
envOverrides.security = {
...config.security,
corsOptions: {
...config.security.corsOptions,
origin: process.env.CORS_ORIGIN.split(','),
},
};
}
return { ...config, ...envOverrides };
}
export default loadConfig();Express Application Setup
javascript
// app.js
import express from 'express';
import { configureResponseHandler } from '@amit-kandar/response-handler';
import config from './config/index.js';
const app = express();
// Apply environment-specific response handler configuration
app.use(configureResponseHandler(config));
// Environment-specific middleware
if (config.environment.current === 'development') {
// Development-specific middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.path} - ${new Date().toISOString()}`);
next();
});
// Mock data endpoint for development
app.get('/api/mock/:resource', (req, res) => {
const mockData = generateMockData(req.params.resource);
res.sendSuccess(mockData, 'Mock data generated');
});
}
if (config.environment.current === 'production') {
// Production-specific middleware
app.use((req, res, next) => {
// Add security headers
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
next();
});
// Health check endpoint
app.get('/health', (req, res) => {
res.sendSuccess({
status: 'healthy',
environment: config.environment.current,
timestamp: new Date().toISOString(),
});
});
}
// API routes
app.get('/api/users', async (req, res) => {
try {
const users = await getUsersFromDatabase();
res.sendPaginated(users, {
page: 1,
limit: 20,
total: users.length,
totalPages: Math.ceil(users.length / 20),
hasNext: false,
hasPrev: false,
});
} catch (error) {
if (config.environment.current === 'development') {
res.sendError('DATABASE_ERROR', error.message, { stack: error.stack });
} else {
res.sendError('DATABASE_ERROR', 'Failed to retrieve users');
}
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT} in ${config.environment.current} mode`);
});Socket.IO Environment Configuration
javascript
// socket.js
import { Server } from 'socket.io';
import { createSocketHandler } from '@amit-kandar/response-handler';
import config from './config/index.js';
export function setupSocketIO(server) {
const io = new Server(server, {
cors: config.security.corsOptions,
pingTimeout: config.environment.current === 'production' ? 60000 : 5000,
pingInterval: config.environment.current === 'production' ? 25000 : 2000,
});
// Apply response handler with environment config
io.use(createSocketHandler(config));
// Environment-specific socket middleware
if (config.environment.current !== 'development') {
// Rate limiting for non-development environments
io.use((socket, next) => {
const clientIp = socket.handshake.address;
// Implement rate limiting logic
next();
});
}
if (config.environment.current === 'production') {
// Production logging
io.use((socket, next) => {
console.log(`Socket connection from ${socket.handshake.address}`);
next();
});
}
io.on('connection', (socket) => {
if (config.environment.current === 'development') {
socket.sendSuccess(
'connection',
{
environment: 'development',
debugging: true,
socketId: socket.id,
},
'Connected to development server',
);
} else {
socket.sendSuccess(
'connection',
{
environment: config.environment.current,
socketId: socket.id,
},
'Connected successfully',
);
}
// Event handlers with environment-specific behavior
socket.on('test_event', (data) => {
if (config.environment.current === 'development') {
socket.sendSuccess(
'test_response',
{
received: data,
timestamp: new Date().toISOString(),
debug: true,
},
'Test event processed (development mode)',
);
} else {
socket.sendSuccess('test_response', data, 'Event processed');
}
});
});
return io;
}Environment Variables (.env files)
bash
# .env.development
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug
DATABASE_URL=postgresql://localhost:5432/myapp_dev
REDIS_URL=redis://localhost:6379
CORS_ORIGIN=http://localhost:3000,http://localhost:3001
DEBUG_MODE=true
# .env.staging
NODE_ENV=staging
PORT=3000
LOG_LEVEL=info
DATABASE_URL=postgresql://staging-db:5432/myapp_staging
REDIS_URL=redis://staging-redis:6379
CORS_ORIGIN=https://staging.myapp.com
DEBUG_MODE=false
# .env.production
NODE_ENV=production
PORT=3000
LOG_LEVEL=error
DATABASE_URL=postgresql://prod-db:5432/myapp_prod
REDIS_URL=redis://prod-redis:6379
CORS_ORIGIN=https://myapp.com
LOG_ENDPOINT=https://logs.myapp.com/api/logs
LOG_API_KEY=your_log_api_key
FRONTEND_URL=https://myapp.com
DEBUG_MODE=falseDocker Environment Configuration
dockerfile
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# Build for production
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]yaml
# docker-compose.yml
version: '3.8'
services:
app-dev:
build: .
ports:
- '3000:3000'
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp_dev
volumes:
- .:/app
- /app/node_modules
depends_on:
- db
- redis
app-staging:
build: .
ports:
- '3001:3000'
environment:
- NODE_ENV=staging
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp_staging
depends_on:
- db
- redis
app-prod:
build: .
ports:
- '3002:3000'
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp_prod
depends_on:
- db
- redis
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
postgres_data:CI/CD Pipeline Configuration
yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main, staging, develop]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
env:
NODE_ENV: test
deploy-dev:
if: github.ref == 'refs/heads/develop'
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to Development
run: |
echo "Deploying to development environment"
env:
NODE_ENV: development
deploy-staging:
if: github.ref == 'refs/heads/staging'
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to Staging
run: |
echo "Deploying to staging environment"
env:
NODE_ENV: staging
deploy-prod:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to Production
run: |
echo "Deploying to production environment"
env:
NODE_ENV: productionConfiguration Validation
javascript
// config/validator.js
import Joi from 'joi';
const configSchema = Joi.object({
logging: Joi.object({
enabled: Joi.boolean().default(true),
level: Joi.string().valid('error', 'warn', 'info', 'debug').required(),
format: Joi.string().valid('json', 'simple').required(),
}).required(),
environment: Joi.object({
current: Joi.string().valid('development', 'staging', 'production').required(),
showStackTrace: Joi.boolean().required(),
exposeInternalErrors: Joi.boolean().required(),
}).required(),
security: Joi.object({
enableCors: Joi.boolean().default(true),
corsOptions: Joi.object().required(),
rateLimiting: Joi.object({
windowMs: Joi.number().min(1000).required(),
maxRequests: Joi.number().min(1).required(),
}).required(),
}).required(),
});
export function validateConfig(config) {
const { error, value } = configSchema.validate(config);
if (error) {
throw new Error(`Configuration validation failed: ${error.message}`);
}
return value;
}Best Practices
- Environment Separation: Keep configurations clearly separated by environment
- Secret Management: Never commit secrets to version control
- Override Capability: Allow environment variables to override config values
- Validation: Validate configuration on startup
- Documentation: Document all configuration options
- Defaults: Provide sensible defaults for optional settings
- Security: Use restrictive settings for production
- Monitoring: Include environment-specific monitoring and logging