Node.js HTTP Server

Node.js HTTP Server

Node.js makes it easy to create HTTP servers for web applications and APIs. This tutorial covers how to build and deploy HTTP servers using Node.js.

What is an HTTP Server?

An HTTP server is software that listens for HTTP requests from clients (like web browsers) and sends back HTTP responses. Node.js provides built-in modules for creating HTTP servers.

Basic HTTP Server

1. Simple Server

Create a basic HTTP server using the built-in http module.

// server.js
const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello, World!');
});

const PORT = 3000;
server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

Run the server:

node server.js

2. Handling Different Routes

Create a server that handles different URL paths.

// server.js
const http = require('http');

const server = http.createServer((req, res) => {
  const url = req.url;
  
  // Set common headers
  res.setHeader('Content-Type', 'text/html');
  
  if (url === '/') {
    res.writeHead(200);
    res.end('<h1>Welcome to the Home Page!</h1>');
  } else if (url === '/about') {
    res.writeHead(200);
    res.end('<h1>About Us</h1><p>We are a Node.js server!</p>');
  } else if (url === '/contact') {
    res.writeHead(200);
    res.end('<h1>Contact</h1><p>Email: [email protected]</p>');
  } else {
    res.writeHead(404);
    res.end('<h1>404 Not Found</h1><p>Page not found!</p>');
  }
});

const PORT = 3000;
server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

3. Serving JSON Data

Create a simple API that returns JSON data.

// api-server.js
const http = require('http');

const users = [
  { id: 1, name: 'John Doe', email: '[email protected]' },
  { id: 2, name: 'Jane Smith', email: '[email protected]' },
  { id: 3, name: 'Bob Johnson', email: '[email protected]' }
];

const server = http.createServer((req, res) => {
  const url = req.url;
  const method = req.method;
  
  // Enable CORS
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  
  if (url === '/api/users' && method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify(users));
  } else if (url.startsWith('/api/users/') && method === 'GET') {
    const id = parseInt(url.split('/')[3]);
    const user = users.find(u => u.id === id);
    
    if (user) {
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify(user));
    } else {
      res.writeHead(404, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'User not found' }));
    }
  } else {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Endpoint not found' }));
  }
});

const PORT = 3000;
server.listen(PORT, () => {
  console.log(`API server running at http://localhost:${PORT}/`);
});

Using Express.js

Express.js is a popular web framework that makes building HTTP servers much easier.

1. Install Express

npm init -y
npm install express

2. Basic Express Server

// express-server.js
const express = require('express');
const app = express();
const PORT = 3000;

// Middleware
app.use(express.json()); // Parse JSON bodies
app.use(express.static('public')); // Serve static files

// Routes
app.get('/', (req, res) => {
  res.send('<h1>Welcome to Express!</h1>');
});

app.get('/about', (req, res) => {
  res.json({ message: 'This is the about page' });
});

app.listen(PORT, () => {
  console.log(`Express server running at http://localhost:${PORT}/`);
});

3. RESTful API with Express

// api.js
const express = require('express');
const app = express();
const PORT = 3000;

// Middleware
app.use(express.json());

// Sample data
let users = [
  { id: 1, name: 'John Doe', email: '[email protected]' },
  { id: 2, name: 'Jane Smith', email: '[email protected]' }
];

// GET all users
app.get('/api/users', (req, res) => {
  res.json(users);
});

// GET user by ID
app.get('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const user = users.find(u => u.id === id);
  
  if (user) {
    res.json(user);
  } else {
    res.status(404).json({ error: 'User not found' });
  }
});

// POST new user
app.post('/api/users', (req, res) => {
  const { name, email } = req.body;
  
  if (!name || !email) {
    return res.status(400).json({ error: 'Name and email are required' });
  }
  
  const newUser = {
    id: users.length + 1,
    name,
    email
  };
  
  users.push(newUser);
  res.status(201).json(newUser);
});

// PUT update user
app.put('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const { name, email } = req.body;
  const userIndex = users.findIndex(u => u.id === id);
  
  if (userIndex !== -1) {
    users[userIndex] = { ...users[userIndex], name, email };
    res.json(users[userIndex]);
  } else {
    res.status(404).json({ error: 'User not found' });
  }
});

// DELETE user
app.delete('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const userIndex = users.findIndex(u => u.id === id);
  
  if (userIndex !== -1) {
    users = users.filter(u => u.id !== id);
    res.status(204).send();
  } else {
    res.status(404).json({ error: 'User not found' });
  }
});

app.listen(PORT, () => {
  console.log(`API server running at http://localhost:${PORT}/`);
});

Middleware

Middleware functions are functions that run during the request-response cycle.

1. Custom Middleware

// middleware.js
const express = require('express');
const app = express();

// Logger middleware
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  next();
};

// Authentication middleware
const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  
  if (token === 'secret-token') {
    req.user = { id: 1, name: 'John Doe' };
    next();
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
};

// Error handling middleware
const errorHandler = (err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
};

// Apply middleware
app.use(logger);
app.use(express.json());

// Protected route
app.get('/api/profile', authenticate, (req, res) => {
  res.json({ user: req.user });
});

// Error middleware (must be last)
app.use(errorHandler);

app.listen(3000, () => {
  console.log('Server with middleware running!');
});

2. Third-party Middleware

// server-with-middleware.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const rateLimit = require('express-rate-limit');

const app = express();

// Security middleware
app.use(helmet()); // Security headers
app.use(cors()); // Enable CORS

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // Limit each IP to 100 requests per windowMs
});
app.use(limiter);

// Logging
app.use(morgan('combined'));

// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.get('/', (req, res) => {
  res.json({ message: 'Server with security middleware!' });
});

app.listen(3000, () => {
  console.log('Secure server running!');
});

Install the middleware packages:

npm install cors helmet morgan express-rate-limit

Serving Static Files

1. Basic Static File Serving

// static-server.js
const express = require('express');
const path = require('path');
const app = express();
const PORT = 3000;

// Serve static files from 'public' directory
app.use(express.static(path.join(__dirname, 'public')));

// API routes
app.get('/api/data', (req, res) => {
  res.json({ message: 'API data' });
});

// Catch-all handler for client-side routing
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.listen(PORT, () => {
  console.log(`Static server running at http://localhost:${PORT}/`);
});

Create a public directory with some files:

public/
├── index.html
├── style.css
└── script.js

2. File Upload Server

// upload-server.js
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const PORT = 3000;

// Configure multer for file uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + path.extname(file.originalname));
  }
});

const upload = multer({ storage });

// Serve uploaded files
app.use('/uploads', express.static('uploads'));

// Upload endpoint
app.post('/upload', upload.single('file'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }
  
  res.json({
    message: 'File uploaded successfully',
    filename: req.file.filename,
    size: req.file.size
  });
});

// Multiple file upload
app.post('/upload-multiple', upload.array('files', 5), (req, res) => {
  if (!req.files || req.files.length === 0) {
    return res.status(400).json({ error: 'No files uploaded' });
  }
  
  const files = req.files.map(file => ({
    filename: file.filename,
    size: file.size
  }));
  
  res.json({
    message: 'Files uploaded successfully',
    files
  });
});

app.listen(PORT, () => {
  console.log(`Upload server running at http://localhost:${PORT}/`);
});

Install multer:

npm install multer

Environment Configuration

1. Using Environment Variables

// config.js
require('dotenv').config();

module.exports = {
  PORT: process.env.PORT || 3000,
  NODE_ENV: process.env.NODE_ENV || 'development',
  DB_HOST: process.env.DB_HOST || 'localhost',
  DB_PORT: process.env.DB_PORT || 5432,
  JWT_SECRET: process.env.JWT_SECRET || 'your-secret-key'
};

Create .env file:

PORT=3000
NODE_ENV=development
DB_HOST=localhost
DB_PORT=5432
JWT_SECRET=your-super-secret-jwt-key

2. Server with Configuration

// server-config.js
const express = require('express');
const config = require('./config');
const app = express();

app.get('/', (req, res) => {
  res.json({
    message: 'Server with environment config',
    environment: config.NODE_ENV,
    port: config.PORT
  });
});

app.listen(config.PORT, () => {
  console.log(`Server running in ${config.NODE_ENV} mode on port ${config.PORT}`);
});

Install dotenv:

npm install dotenv

Error Handling

1. Comprehensive Error Handling

// error-handling-server.js
const express = require('express');
const app = express();

// Custom error class
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    
    Error.captureStackTrace(this, this.constructor);
  }
}

// Async error wrapper
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Routes that might throw errors
app.get('/error', (req, res, next) => {
  throw new AppError('This is a test error', 500);
});

app.get('/async-error', asyncHandler(async (req, res) => {
  // Simulate async error
  throw new AppError('Async error occurred', 400);
}));

app.get('/not-found', (req, res, next) => {
  const error = new AppError('Resource not found', 404);
  next(error);
});

// 404 handler
app.use('*', (req, res, next) => {
  const error = new AppError(`Can't find ${req.originalUrl} on this server!`, 404);
  next(error);
});

// Global error handler
app.use((err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  err.status = err.status || 'error';

  res.status(err.statusCode).json({
    status: err.status,
    error: err,
    message: err.message,
    stack: err.stack
  });
});

app.listen(3000, () => {
  console.log('Server with error handling running!');
});

Production Best Practices

1. Production Server Setup

// production-server.js
const express = require('express');
const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  const numCPUs = os.cpus().length;
  
  console.log(`Master ${process.pid} is running`);
  console.log(`Forking ${numCPUs} workers`);
  
  // Fork workers
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork(); // Restart worker
  });
} else {
  const app = express();
  
  app.get('/', (req, res) => {
    res.json({
      message: 'Production server',
      worker: process.pid,
      timestamp: new Date().toISOString()
    });
  });
  
  const PORT = process.env.PORT || 3000;
  app.listen(PORT, () => {
    console.log(`Worker ${process.pid} started on port ${PORT}`);
  });
}

2. Graceful Shutdown

// graceful-shutdown.js
const express = require('express');
const app = express();

let server;

const startServer = () => {
  server = app.listen(3000, () => {
    console.log('Server started');
  });
};

const gracefulShutdown = (signal) => {
  console.log(`Received ${signal}. Starting graceful shutdown`);
  
  server.close(() => {
    console.log('HTTP server closed');
    process.exit(0);
  });
  
  // Force close after 10 seconds
  setTimeout(() => {
    console.error('Could not close connections in time, forcefully shutting down');
    process.exit(1);
  }, 10000);
};

// Handle shutdown signals
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

startServer();

Testing Your Server

1. Using curl

# Test GET request
curl http://localhost:3000/

# Test POST request with JSON
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"John","email":"[email protected]"}'

# Test with authentication
curl -H "Authorization: Bearer token" http://localhost:3000/api/profile

2. Using Postman/Insomnia

Import these collections for testing:

{
  "info": {
    "name": "Node.js API Tests"
  },
  "item": [
    {
      "name": "Get All Users",
      "request": {
        "method": "GET",
        "url": "http://localhost:3000/api/users"
      }
    },
    {
      "name": "Create User",
      "request": {
        "method": "POST",
        "url": "http://localhost:3000/api/users",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\"name\":\"John Doe\",\"email\":\"[email protected]\"}"
        }
      }
    }
  ]
}

External Resources:

Related Tutorials:

Last updated on