7. How do you create a basic HTTP server in Node.js?

Basic

7. How do you create a basic HTTP server in Node.js?

Overview

Creating a basic HTTP server in Node.js is a foundational skill for backend development. It allows developers to understand how web applications handle requests and responses at a low level. This knowledge is crucial for building scalable and efficient web applications and APIs.

Key Concepts

  1. HTTP Module: Node.js provides an HTTP module used to create servers and clients.
  2. Request and Response Objects: Understanding how to work with request and response objects is key to manipulating data and headers exchanged between client and server.
  3. Asynchronous Programming: Node.js operates on a non-blocking, event-driven architecture, making asynchronous programming a core concept in handling HTTP requests.

Common Interview Questions

Basic Level

  1. How do you create a simple HTTP server in Node.js?
  2. What are the main methods of the HTTP module to handle requests and responses?

Intermediate Level

  1. How can you serve static files using a Node.js HTTP server?

Advanced Level

  1. How would you optimize a Node.js HTTP server for high traffic?

Detailed Answers

1. How do you create a simple HTTP server in Node.js?

Answer: You can create a simple HTTP server in Node.js using the http module. This module provides a method called createServer which takes a callback function. This callback function is executed every time the server receives a request. The callback function has two parameters, req (request) and res (response), which are used to handle incoming requests and send responses back to the client.

Key Points:
- The http module is built-in and does not require an external installation.
- The req object represents the incoming request, including HTTP headers and payload data.
- The res object is used to send back the desired response, including setting headers and the response body.

Example:

// Import the http module from Node.js
const http = require('http');

// Create an HTTP server
const server = http.createServer((req, res) => {
  // Set the Content-Type header
  res.setHeader('Content-Type', 'text/plain');
  // Send a response back to the client
  res.end('Hello, world!');
});

// Listen on port 3000
server.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

2. What are the main methods of the HTTP module to handle requests and responses?

Answer: The HTTP module provides several methods for handling requests and responses, but the most commonly used are:

  • createServer(): Creates a new HTTP server object.
  • writeHead(statusCode, [statusMessage], [headers]): Sends a response header to the request.
  • end([data], [encoding], [callback]): Signals to the server that all of the response headers and body have been sent; that server should consider this message complete.

Key Points:
- The createServer method is essential for setting up the server to accept connections.
- The writeHead method allows you to set the response status code and headers before sending the response body.
- The end method finishes the response process and can optionally take data to send as the last part of the response.

Example:

const http = require('http');

const server = http.createServer((req, res) => {
  // Set HTTP status to 200 and Content-Type to text/plain
  res.writeHead(200, {'Content-Type': 'text/plain'});
  // Send the response body "Hello, world!" and end the response
  res.end('Hello, world!');
});

server.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

3. How can you serve static files using a Node.js HTTP server?

Answer: To serve static files like HTML, CSS, and JavaScript, you can use the fs (File System) module to read files from the file system and serve them in response to HTTP requests. It's important to set the appropriate content type in the response headers based on the file type.

Key Points:
- Use the fs.readFile method to asynchronously read the content of a file.
- It's crucial to handle file not found or other errors to avoid server crashes.
- The content type in the response header should match the type of the static file being served.

Example:

const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
  // Assuming a request to 'index.html'
  const filePath = path.join(__dirname, 'public', req.url === '/' ? 'index.html' : req.url);
  const extname = path.extname(filePath);
  let contentType = 'text/html'; // Default content type

  // Set content type based on file extension
  switch (extname) {
    case '.css':
      contentType = 'text/css';
      break;
    case '.js':
      contentType = 'text/javascript';
      break;
    // Add more cases for other content types as needed
  }

  fs.readFile(filePath, (error, content) => {
    if (error) {
      if (error.code == 'ENOENT') {
        // Handle file not found, e.g., serve a 404 page
        fs.readFile(path.join(__dirname, 'public', '404.html'), (error, content) => {
          res.writeHead(404, { 'Content-Type': 'text/html' });
          res.end(content, 'utf-8');
        });
      } else {
        // Some server error
        res.writeHead(500);
        res.end(`Server error: ${error.code}`);
      }
    } else {
      // Success, serve file
      res.writeHead(200, { 'Content-Type': contentType });
      res.end(content, 'utf-8');
    }
  });
});

server.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

4. How would you optimize a Node.js HTTP server for high traffic?

Answer: Optimizing a Node.js HTTP server for high traffic involves several strategies, including but not limited to:

  • Clustering: Utilize the Node.js cluster module to create child processes that share server ports, enabling load balancing over multiple CPU cores.
  • Caching: Implement caching mechanisms to reduce the load on the server by serving cached content for frequently requested resources.
  • Load Balancing: Use a reverse proxy like NGINX to distribute traffic evenly across multiple instances of the Node.js application.
  • Asynchronous Code: Ensure that your code is non-blocking by making use of asynchronous programming patterns and promises to handle I/O operations efficiently.
  • Performance Monitoring: Use tools like PM2 for process management and monitoring the application's health and performance.

Key Points:
- Clustering allows Node.js applications to take full advantage of multi-core systems for better performance.
- Caching and load balancing can significantly reduce response times and server load.
- Monitoring tools are essential for identifying bottlenecks and performance issues in real-time.

Example:

const http = require('http');
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (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`);
  });
} else {
  // Workers can share any TCP connection
  // In this case, it is an HTTP server
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

This example demonstrates using the cluster module to fork multiple worker processes that can all share the same server port. This is a foundational strategy for optimizing a Node.js application to handle high traffic efficiently.