CLOSE

Node.js includes a built-in HTTP module that allows us to create web servers and make HTTP requests without needing third-party libraries.

Understanding the HTTP Module

The HTTP module offers:

  1. Server-Side Capabilities:
    • Create an HTTP server that listens for incoming requests, processes them, and sends back responses.
  2. Client-Side Capabilities:
    • Make HTTP requests to other servers. This is useful for consuming APIs or integrating with external services.
  3. Event-Driven Model:
    • Both servers and clients built with the HTTP module use Node.js's event-driven architecture. This means you can attach event listeners to handle data, errors, and end-of-response events.

The HTTP module is a core part of Node.js, meaning it comes out-of-the-box with no need for installation. Its low-level API provides flexibility for custom server behavior, making it a perfect learning tool before moving on to higher-level frameworks like Express.

Creating an HTTP Server

Let’s start with a simple example to demonstrate how to create an HTTP server using Node.js.

const http = require('http'); // Import the HTTP module

const hostname = '127.0.0.1'; // Localhost address
const port = 3000;          // Server will listen on port 3000

// Create a server that responds to every request
const server = http.createServer((req, res) => {
  // Set the status code and content type for the response
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  
  // Send a simple response message
  res.end('Hello, World!\n');
});

// Start the server and log that it’s running
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

How It Works:

  1. Import the Module:
    We begin by requiring the http module.
  2. Create the Server:
    Using http.createServer(), we define a callback function that receives the request (req) and response (res) objects. This callback is executed every time the server receives an HTTP request.
  3. Set Response Headers and Body:
    We set the response status to 200 OK and define the Content-Type as plain text. Finally, we end the response with a simple message.
  4. Listen for Connections:
    The server listens on the specified hostname and port. When ready, it logs a confirmation message.

Handling Different Routes and Response

A more dynamic server often needs to serve different responses based on the URL path. Let’s extend our basic server to handle multiple routes.

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  // Log the request URL for debugging
  console.log(`Request for ${req.url} received.`);

  // Set up basic routing logic
  if (req.url === '/') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html');
    res.end('<h1>Welcome to the Home Page</h1>');
  } else if (req.url === '/about') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html');
    res.end('<h1>About Us</h1><p>This is the about page.</p>');
  } else {
    res.statusCode = 404;
    res.setHeader('Content-Type', 'text/plain');
    res.end('404 Not Found');
  }
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Key Points:

  • Route Checking:
    The server checks the req.url property to determine which route was requested.
  • Dynamic Responses:
    Depending on the route, the server responds with HTML content for the home and about pages, and a plain-text message for unknown routes.
  • Logging:
    Logging incoming requests can be very helpful for debugging and monitoring.

Making HTTP Requests

Apart from serving content, Node.js can act as a client making HTTP requests. The HTTP module provides a method called http.request() for this purpose

const http = require('http');

// Define request options
const options = {
  hostname: 'www.example.com',
  port: 80,
  path: '/',
  method: 'GET'
};

// Create the request
const req = http.request(options, res => {
  console.log(`Status Code: ${res.statusCode}`);
  
  // Collect response data in chunks
  let data = '';
  res.on('data', chunk => {
    data += chunk;
  });
  
  // When the response is finished, log the complete response
  res.on('end', () => {
    console.log('Response Body:', data);
  });
});

// Handle request errors
req.on('error', error => {
  console.error('Request Error:', error);
});

// End the request (this sends it)
req.end();

How It Works:

  1. Options Object:
    The options object specifies the hostname, port, path, and HTTP method for the request.
  2. Creating the Request:
    The http.request() function returns an instance of the ClientRequest object. We attach event listeners to capture the response.
  3. Event Listeners:
    • The data event accumulates chunks of data as they arrive.
    • The end event signals that all data has been received.
    • The error event handles any issues during the request.
  4. Sending the Request:
    Calling req.end() finalizes and sends the request.