bugl
bugl
HomeLearnPatternsPathsSearch
HomeLearnPatternsPathsSearch

Loading lesson path

Learn/Node.js/Node.js Deployment
Node.js•Node.js Deployment

Node.js: Development vs Production

Flash cards

Review the key moves

1/4
Core idea

What is the main idea behind Node.js: Development vs Production?

Lesson checks

Practice each idea before moving on

Short Mimo-style checks built from this lesson's code, terms, and sequence.

1Quick choice

Which statement best captures the main point of this lesson?

2Fill blank

Complete the missing token from the example code.

# ___ Command Prompt
3Order

Put the learning moves in the order that makes the concept easiest to apply.

The NODE_ENV Environment Variable
Key Differences at a Glance
Differences between Development and Production

Differences between Development and Production

This page covers the key differences between development and production environments in Node.js applications and best practices for managing both effectively.

Key Differences at a Glance

  • Verbose logging
  • Detailed error messages
  • Hot-reloading enabled
  • Unminified code
  • Mock data/stubs
  • Minimal logging
  • Generic error messages
  • Optimized performance
  • Minified and bundled code
  • Real data/services

The NODE_ENV Environment Variable

In Node.js, the NODE_ENV environment variable is a convention used to determine the environment in which an application is running.

It's commonly set to either 'development' or 'production', though other values like 'test' or 'staging' are also used.

Note

Many Node.js frameworks and libraries (like Express, React, Vue, etc.) use NODE_ENV to enable or disable certain features and optimizations.

Setting NODE_ENV

# Windows Command Prompt
set NODE_ENV=production
node app.js
# Windows PowerShell
$env:NODE_ENV="production"
node app.js
# Linux/macOS
export NODE_ENV=production
node app.js
{
 "scripts": {
 "start": "NODE_ENV=production node app.js",
 "dev": "NODE_ENV=development nodemon app.js",
 "test": "NODE_ENV=test jest"
 }
}
npm install --save-dev cross-env
{
 "scripts": {
 "start": "cross-env NODE_ENV=production node app.js"
 }
}

Using NODE_ENV in Your Application

// Simple environment check
const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = !isProduction;
// Environment-specific configuration
const config = {
 port: process.env.PORT || 3000,
 db: {
 host: isProduction ? 'prod-db.example.com' : 'localhost',
 name: isProduction ? 'myapp_prod' : 'myapp_dev'
 },
 logging: {
 level: isProduction ? 'warn' : 'debug',
 prettyPrint: !isProduction
 }
};
// Express.js example
const express = require('express');
const app = express();

Note

Setting NODE_ENV=production can improve application performance by up to 35%, as some packages apply optimizations based on this setting.

Configuration Management

Different environments typically require different configurations for databases, APIs, logging, and other services.

Configuration with dotenv

// Install dotenv: npm install dotenv
require('dotenv').config(); // Loads .env file contents into process.env
// config.js
module.exports = {
 development: {
 port: 8080,
 database: 'mongodb://localhost:27017/myapp_dev',
 logLevel: 'debug',
 apiKeys: {
 thirdPartyService: process.env.DEV_API_KEY
 }
 },
 test: {
 port: 3001,
 database: 'mongodb://localhost:27017/myapp_test',
 logLevel: 'info',
 apiKeys: {
 thirdPartyService: process.env.TEST_API_KEY
 }
 },
 production: {
 port: process.env.PORT || 8080,
 database: process.env.DATABASE_URL,
 logLevel: 'error',
 apiKeys: {
 thirdPartyService: process.env.PROD_API_KEY
 }
 }
};
const env = process.env.NODE_ENV || 'development';
module.exports.current = module.exports[env];

Configuration Files

Common approaches to configuration management include

  • Environment files: Using .env files with the dotenv package
  • Configuration objects: Creating environment-specific configuration objects
  • Configuration services: Using external services like AWS Parameter Store, Vault, or Consul

Security Warning: Never commit sensitive information like API keys, database credentials, or secrets to version control. Always use environment variables or secure configuration services for sensitive data.

Error Handling

Error handling strategies should differ between development and production environments:

  • Display detailed error messages and stack traces
  • Use verbose logging to aid debugging
  • Crash early on errors to identify issues quickly
  • Enable source maps for better debugging
  • Provide interactive debugging tools
  • Hide implementation details from error responses
  • Log errors for internal use but return generic error messages
  • Implement proper error recovery mechanisms
  • Use structured logging for better analysis
  • Implement circuit breakers for external services

Environment-Specific Error Handling

const express = require('express');
const app = express();
app.get('/api/data', (req, res) => {
 try {
 // Some operation that might fail
 throw new Error('Something went wrong');
 } catch (error) {
 // Log the error internally (always do this)
 console.error('Error occurred:', error);
 // Provide different responses based on environment
 if (process.env.NODE_ENV === 'production') {
 // In production: generic error message
 return res.status(500).json({
 error: 'An unexpected error occurred'
 });
 } else {
 // In development: detailed error information
 return res.status(500).json({
 error: error.message,
 stack: error.stack,
 details: 'This detailed error is only shown in development'
 });
}
}
});
app.listen(8080);

Logging Strategies

Logging requirements differ significantly between development and production:

  • Verbose logging with detailed information
  • Human-readable format with colors
  • Console output for immediate feedback
  • Debug and trace level logging enabled
  • No log rotation needed
  • Structured logging (JSON format)
  • Appropriate log levels (warn/error)
  • Log rotation and retention policies
  • Centralized log aggregation
  • Performance monitoring integration

Environment-Specific Logging with Winston

const winston = require('winston');
// Define different logging configurations per environment
const logger = winston.createLogger({
 level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
 format: process.env.NODE_ENV === 'production'
 ? winston.format.json()
 : winston.format.combine(
 winston.format.colorize(),
 winston.format.simple()
),
defaultMeta: { service: 'user-service' },
transports: [
 // Always log errors to a file
 new winston.transports.File({
 filename: 'error.log',
 level: 'error'
 }),
 // In production, save all logs to a file
 ...(process.env.NODE_ENV === 'production'
 ? [new winston.transports.File({ filename: 'combined.log' })]
 : []),
 // In development, log to the console
 ...(process.env.NODE_ENV !== 'production'
 ? [new winston.transports.Console()]
 : [])
],
});
logger.info('Application started');
logger.debug('This debug message appears only in development');

For more details on logging, see the Node.js Logging tutorial.

Performance Optimizations

Production environments should be optimized for performance and reliability:

  • Fast refresh and hot module replacement
  • Source maps for debugging
  • Detailed error overlays
  • Unminified code
  • Development tools integration
  • Code minification and tree-shaking
  • Asset optimization and compression
  • Content Delivery Network (CDN) usage
  • Browser caching headers
  • Performance monitoring

Performance Considerations

  • Optimized code bundling and minification
  • Caching strategies
  • Cluster mode to utilize multiple CPU cores
  • Memory and CPU optimizations
  • Content compression

Running in Cluster Mode in Production

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (process.env.NODE_ENV === 'production' && cluster.isMaster) {
 console.log(`Master ${process.pid} is running`);
 // Fork workers
 for (let i = 0; i < numCPUs; i++) {
 cluster.fork();
 }
 cluster.on('exit', (worker, code, signal) => {
 console.log(`Worker ${worker.process.pid} died`);
 // Replace the dead worker
 cluster.fork();
 });
} else {
// Workers can share a TCP connection
http.createServer((req, res) => {
 res.writeHead(200);
 res.end('Hello World\n');
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}

Security Considerations

Security practices should be more stringent in production environments:

Production Security Measures

  • Helmet: Set security-related HTTP headers
  • Rate limiting: Protect against brute force and DoS attacks
  • CSRF protection: Prevent cross-site request forgery
  • Input validation: Sanitize all user inputs
  • HTTPS: Encrypt all communications
  • Dependency scanning: Check for vulnerabilities in dependencies

Security Middleware for Production

const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const app = express();
// Apply security middleware in production
if (process.env.NODE_ENV === 'production') {
 // Set security headers
 app.use(helmet());
 // Enable rate limiting
 const limiter = rateLimit({
 windowMs: 15 * 60 * 1000, // 15 minutes
 max: 100, // limit each IP to 100 requests per windowMs
 message: 'Too many requests from this IP, please try again later'
 });
 app.use('/api/', limiter);
 // Force HTTPS
 app.use((req, res, next) => {
 if (req.header('x-forwarded-proto') !== 'https') {
 res.redirect(`https://${req.header('host')}${req.url}`);
 } else {
 next();
 }
});
}
app.get('/', (req, res) => {
 res.send('Hello World');
});
app.listen(8080);

Build Process

For applications using TypeScript, Babel, or other build tools, the build process differs between environments:

Development Build

  • Source maps for debugging
  • Incremental compilation
  • Hot module replacement
  • Less aggressive optimizations

Production Build

  • Minification and tree shaking
  • Bundling and code splitting
  • Ahead-of-time compilation
  • No source maps (or external source maps)

webpack.config.js for Different Environments

const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = (env, argv) => {
 const isProduction = argv.mode === 'production';
 return {
 entry: './src/index.js',
 output: {
 path: path.resolve(__dirname, 'dist'),
 filename: isProduction
 ? 'bundle.[contenthash].js'
 : 'bundle.js'
 },
 mode: isProduction ? 'production' : 'development',
 // Generate source maps in development but not production
 devtool: isProduction ? false : 'eval-source-map',
 optimization: {
 minimize: isProduction,
 minimizer: isProduction ? [
 new TerserPlugin({
 terserOptions: {
 compress: true,
 mangle: true
 }
 })
 ] : [],
 },
 // Add development server configuration for non-production
 ...(isProduction ? {} : {
 devServer: {
 contentBase: './dist',
 hot: true
 }
 })
 };
};

Package.json Scripts

{
 "scripts": {
 "start": "node dist/server.js",
 "dev": "nodemon src/server.ts",
 "build": "tsc",
 "build:prod": "tsc && webpack --mode=production",
 "lint": "eslint src/**/*.ts",
 "test": "jest"
 }
}

Development

  • Local development server
  • Docker containers for dependencies
  • Integrated development environment (IDE) tools

Previous

Node.js Environment Variables

Next

Node.js CI/CD