Node.js HTTP Module

The http module is a core pillar of the Node.js ecosystem. It provides the low-level functionality required to transfer data over the HyperText Transfer Protocol (HTTP). While many developers eventually move to higher-level frameworks like Express or Fastify, understanding the native http module is crucial because those frameworks are built directly on top of it. It allows you to transform your computer into a functional web server capable of delivering content to users across the globe.

Developer Tip: Even if you plan on using a framework like Express, learning the native HTTP module helps you understand how Node.js handles streams, buffers, and networking under the hood.

 

Key Features of the HTTP Module

  1. Creating HTTP Servers: You can spin up a lightweight server instance with just a few lines of code to listen for network traffic.
  2. Handling Requests: The module provides an IncomingMessage object, allowing you to inspect URLs, headers, and body data sent by a client.
  3. Sending Responses: It gives you full control over the ServerResponse, allowing you to set custom status codes, manipulate headers, and stream content back to the user.
  4. Client-Side Requests: Beyond building servers, the http module can also act as a client, allowing your application to fetch data from external APIs.

 

Creating an HTTP Server

1. http.createServer()

The http.createServer() method is the primary way to initialize a server. It takes a callback function, often called the "request listener," which executes every time a user makes a request to your server.

const http = require('http');

// The callback provides two objects: 'req' (request) and 'res' (response)
const server = http.createServer((req, res) => {
  // Set the HTTP status and response headers
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  
  // Send the body content and close the connection
  res.end('Hello, World!\n');
});

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

Output:

Server running at http://localhost:3000/
  • When you visit http://localhost:3000 in your browser, the res.end() method signals to the server that the response is complete and sends the "Hello, World!" string to the client.
Common Mistake: Forgetting to call res.end(). If you don't call this method, the browser will keep waiting for more data until it eventually times out, making your application feel frozen.

 

Handling Different Request Methods

In a real-world application, you won't just send one message. You'll need to distinguish between different HTTP verbs like GET (fetching data) and POST (submitting data).

1. Handling GET Requests

GET requests are typically used to retrieve information. You can use req.url and req.method to route the user to the correct logic.

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/info') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ message: "This is a GET request to the info route" }));
  } else {
    res.writeHead(404);
    res.end('Not Found');
  }
});

server.listen(3000);
Best Practice: Always provide a fallback or "default" response. If a user visits a URL you haven't defined, send a 404 status code so the client knows the resource doesn't exist.

2. Handling POST Requests

POST requests are more complex because the data arrives in chunks. Since Node.js is non-blocking, it handles large data uploads by receiving small pieces of data at a time. You must assemble these pieces (buffers) into a complete body.

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.method === 'POST') {
    let body = '';

    // Listen for data chunks
    req.on('data', chunk => {
      body += chunk.toString();
    });

    // Listen for the end of the data stream
    req.on('end', () => {
      console.log('Received Body:', body);
      res.writeHead(201, { 'Content-Type': 'text/plain' });
      res.end(`Data received successfully: ${body}`);
    });
  }
});

server.listen(3000);
Watch Out: When handling POST data, be careful about memory. Malicious users could send massive amounts of data to crash your server. Always implement a limit on the body size in production apps.

 

Sending HTTP Responses

The response object (res) allows you to communicate the result of a request using status codes. This is vital for letting the client know if things went well or if there was an error.

1. Sending a 404 Error

If a user requests a page that doesn't exist, you should explicitly set the status code to 404.

const http = require('http');

const server = http.createServer((req, res) => {
  // Simulate a missing page
  res.writeHead(404, { 'Content-Type': 'text/html' });
  res.end('<h1>404: Page Not Found</h1>');
});

server.listen(3000);
Developer Tip: Use standard HTTP status codes: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), and 500 (Internal Server Error). This makes your API predictable for other developers.

 

Making HTTP Requests

Node.js can also act as a client. This is useful when your server needs to talk to another service, like a weather API or a payment gateway.

1. Making a GET Request to an External API

const http = require('http');

// Example: Calling a public API
http.get('http://jsonplaceholder.typicode.com/posts/1', (res) => {
  let data = '';

  // Collect the data chunks
  res.on('data', (chunk) => {
    data += chunk;
  });

  // Process the complete response
  res.on('end', () => {
    const post = JSON.parse(data);
    console.log('Post Title:', post.title);
  });

}).on('error', (err) => {
  console.log('Error: ' + err.message);
});
  • This script performs an outgoing GET request. Note that we handle the 'error' event—this is a best practice to prevent your application from crashing if the external server is down.

 

Summary

The http module is the backbone of web development in Node.js. It gives you granular control over how your server interacts with the outside world. By mastering http.createServer, request routing, and response management, you gain a deep understanding of how the web works. While you might use Express for larger projects to save time, the principles you learned here—handling streams, status codes, and headers—will remain the same across every tool you use in the Node.js ecosystem.