Skip to main content

Command Palette

Search for a command to run...

Understanding HTTP Requests, Responses, and HTTP Methods using Node.js

Updated
14 min read
Understanding HTTP Requests, Responses, and HTTP Methods using Node.js
S

I'm Shubham (@shubhamsinghbundela), I'm a Software Engineer, a Full-stack developer, a tech enthusiast, and a technical writer here on @Hashnode. I have a strong zeal to share my acquired knowledge and I am also willing to learn from others.

When building backend applications, one of the most fundamental concepts to understand is how clients communicate with servers.

This communication happens using the HTTP.

In this blog, we will learn:

  • What HTTP Request is

  • What HTTP Response is

  • What HTTP Methods are

  • How to implement them using Node.js

  • How to test them using Postman

We will build a simple Todo backend server using the Node.js http module.


1. Understanding HTTP Request

An HTTP request is sent by the client ((browser, mobile app, or tools like Postman) to the server asking it to perform some action.

A request contains:

Component Description
Method Specifies the action to perform (GET, POST, DELETE, etc.)
URL The resource or endpoint the client wants to access
Headers Additional metadata about the request
Query Parameters Extra data passed in the URL
Body The main data sent to the server (mostly used in POST/PUT/PATCH requests)

1. HTTP Method

The method tells the server what operation the client wants to perform.

Example:

GET /todos
POST /create/todo
DELETE /todo?id=1

Common HTTP methods include:

  • GET → Fetch data

  • POST → Create new data

  • PUT → Replace data

  • PATCH → Update part of data

  • DELETE → Remove data

These methods are part of the HTTP.


2. URL (Endpoint)

The URL identifies the resource the client wants.

Example:

http://localhost:3000/todos

Here:

  • http → protocol

  • localhost:3000 → server

  • /todos → endpoint


3. Headers

Headers provide extra information about the request.

Example headers:

Content-Type: application/json
Authorization: Bearer token
User-Agent: PostmanRuntime

Common headers:

Header Purpose
Content-Type Type of data sent
Authorization Authentication token
Accept Expected response format

4. Query Parameters

Query parameters send additional information through the URL.

Example:

GET /todo?id=1

Here:

id=1

is the query parameter.

In Node.js we read it like this:

const urlData = url.parse(req.url, true);
const todoId = urlData.query.id;

5. Request Body

The body contains the main data sent by the client to the server.

It is mostly used with:

  • POST

  • PUT

  • PATCH

Example JSON body:

{
 "title": "Write Blog",
 "description": "Explain HTTP methods"
}

This data is used by the server to create or update resources.

In Node.js, the request body arrives as chunks (streams), so we must collect them before converting them to JSON.

Example:

const bodyStream = [];

req.on("data", (chunk) => {
  bodyStream.push(chunk);
});

req.on("end", () => {
  const body = JSON.parse(Buffer.concat(bodyStream).toString());
});

2. Understanding HTTP Response

An HTTP response is what the server sends back after processing the request.

A response tells the client:

  • Whether the request succeeded or failed

  • What data is being returned

  • Additional information about the response

A typical HTTP response contains the following components.

Component Description
Status Code Indicates success or failure
Response Headers Metadata about the response
Body The actual data returned

1. Status Code

The status code tells the client whether the request succeeded or failed.

Common status codes:

Code Meaning
200 Request successful
201 Resource created
400 Bad request
401 Unauthorized
404 Resource not found
500 Internal server error

Example in Node.js:

res.statusCode = 200;

2. Response Headers

Response headers provide extra information about the response.

Example:

Content-Type: application/json
Content-Length: 120
Cache-Control: no-cache

Common response headers:

Header Purpose
Content-Type Type of data returned
Content-Length Size of response body
Cache-Control Controls caching
Set-Cookie Stores cookies in browser

Example in Node.js:

res.setHeader("Content-Type", "application/json");

3. Response Body

The response body contains the actual data returned by the server.

Example JSON response:

{
 "id": 1,
 "title": "Write Blog",
 "description": "Explain HTTP response"
}

Example in Node.js:

res.end(JSON.stringify(data));

3. HTTP Methods Explained

HTTP methods define what operation the client wants to perform.

The most common methods are:

Method Purpose
GET Retrieve data
POST Create new data
PUT Replace existing data
PATCH Update part of data
DELETE Remove data

4. Setting Up the Node.js HTTP Server

We will use Node's built-in http module.

const http = require("http");
const url = require("url");

5. Creating the HTTP Server

const server = http.createServer((req, res) => {

});

This callback receives two important objects:

Object Description
req Represents the HTTP request sent by the client
res Represents the HTTP response that the server will send back

req — Request Object

The req object contains all the information about the incoming request from the client.

Using this object, we can access:

  • The HTTP method (req.method)

  • The request URL (req.url)

  • Request headers (req.headers)

  • Request body (via stream events - I explained below)

res — Response Object

The res object is used to send a response back to the client.

With this object we can:

  • Set the status code

  • Set response headers

  • Send the response body

Example:

res.statusCode = 200;
res.setHeader("Content-Type", "text/plain");
res.end("Hello World");

Here:

  • statusCode tells the client whether the request was successful.

  • setHeader() sends metadata about the response.

  • res.end() sends the final response body and completes the request.


6. GET Method Example

In HTTP, the GET method is used to retrieve data from the server without modifying anything on the server.

In this example, when a client sends a request to the root route /, the server returns a simple response: "Hello World".

Code Example

const http = require("http");
const url = require("url");

const server = http.createServer((req, res) => {
  const urlData = url.parse(req.url, true);

  if (urlData.pathname === "/" && req.method === "GET") {
    res.statusCode = 200;
    res.setHeader("Content-Type", "text/plain");
    return res.end("Hello World");
  }
});

Step-by-Step Explanation of Above Code

1. Importing Required Modules

const http = require("http");
const url = require("url");

Here we import two built-in modules from Node.js:

Module Purpose
http Used to create the HTTP server
url Helps parse the request URL and query parameters

2. Creating the HTTP Server

const server = http.createServer((req, res) => {

});

createServer() creates a server that listens for incoming requests.

The callback function runs every time a client sends a request.

It receives two objects:

Object Description
req Contains information about the incoming request
res Used to send the response back to the client

3. Parsing the Request URL

const urlData = url.parse(req.url, true);

Here we parse the request URL so we can easily access different parts of it.

Example request:

http://localhost:3000/

After parsing:

Property Value
urlData.pathname /
urlData.query {}

This helps us determine which route the client is requesting.


4. Checking Route and HTTP Method

if (urlData.pathname === "/" && req.method === "GET")

Here we check two things:

Condition Purpose
urlData.pathname === "/" Client requested the root route
req.method === "GET" Client used the GET HTTP method

Both conditions must be true for this block to execute.


5. Setting the Status Code

res.statusCode = 200;

The status code 200 means:

The request was successful.


6. Setting Response Headers

res.setHeader("Content-Type", "text/plain");

This header tells the client:

The response body contains plain text.


7. Sending the Response

return res.end("Hello World");

res.end():

  • Sends the response body

  • Ends the response

So the client receives:

Hello World

Testing This Route

You can test this using Postman.

Request

Method:

GET

URL:

http://localhost:3000/

Response

Hello World

Status Code:

200 OK

Request–Response Flow

Client (Browser/Postman)
        │
        │  GET /
        ▼
Server
        │
        │  200 OK
        │  Hello World
        ▼
Client

7. POST Method Example

POST /create/todo

The POST method is used to send data from the client to the server in order to create a new resource.

In this example, we will create a new Todo item by sending title and description in the request body.


Code Example

if (urlData.pathname === "/create/todo" && req.method === "POST") {
  const bodyStream = [];

  req.on("data", (chunk) => {
    bodyStream.push(chunk);
  });

  req.on("end", () => {
    let body;

    try {
      body = JSON.parse(Buffer.concat(bodyStream).toString());
    } catch {
      res.statusCode = 400;
      return res.end(JSON.stringify({ error: "Invalid JSON" }));
    }


    res.statusCode = 200;
    res.setHeader("Content-Type", "application/json");
    res.end(JSON.stringify(body));
  });
}

1. Checking Route and HTTP Method

if (urlData.pathname === "/create/todo" && req.method === "POST")

This ensures that the server processes the request only when:

  • The request is sent to the correct endpoint

  • The HTTP method is POST

This is important because the same server may handle multiple routes and methods.


2. Creating a Container for Request Body

const bodyStream = [];

In Node.js, the request body is received as a stream of data, not as a single object.

So we create an array to temporarily store incoming chunks of data.


3. Listening for Incoming Data

req.on("data", (chunk) => {
  bodyStream.push(chunk);
});

Here we listen for the data event.

Every time part of the request body arrives:

  • Node.js emits a data event

  • The chunk variable contains a small piece of the request body

  • We store each chunk inside the bodyStream array

This happens because request bodies are streamed for better performance and memory usage.


4. Detecting When the Request Body Is Complete

req.on("end", () => {

The end event is triggered when all chunks of the request body have been received.

Only after this event fires can we safely process the full request body.


5. Combining All Chunks

Buffer.concat(bodyStream).toString()

This step converts the collected chunks into a readable string.

Explanation:

Step Purpose
Buffer.concat(bodyStream) Combines all chunks into a single buffer
.toString() Converts the buffer into a readable string

Example result:

{"title":"Write Blog","description":"Explain HTTP methods"}

6. Converting the String to JSON

body = JSON.parse(Buffer.concat(bodyStream).toString());

Most APIs send request bodies in JSON format, so we convert the string into a JavaScript object.

Example result:

{
  title: "Write Blog",
  description: "Explain HTTP methods"
}

Now we can access values like:

body.title
body.description

7. Handling Invalid JSON

try {
  body = JSON.parse(...)
} catch {
  res.statusCode = 400;
  return res.end(JSON.stringify({ error: "Invalid JSON" }));
}

If the client sends invalid JSON, JSON.parse() will throw an error.

Using try...catch prevents the server from crashing and returns a proper 400 Bad Request response.


8. Sending the Response

res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(body));

Here we send the response back to the client.

Line Purpose
statusCode = 200 Indicates request was successful
Content-Type Specifies response format
res.end() Sends the response and ends the request

In this example, the server simply returns the parsed request body.


Key Takeaway

When using the Node.js http module to handle POST requests, we must manually:

  1. Listen for incoming data chunks

  2. Store them in a container

  3. Detect when all data has arrived

  4. Combine the chunks

  5. Convert the body into JSON

Frameworks like Express.js handle these steps automatically, but understanding this process helps developers see how HTTP body parsing works internally.


Let's Build a Complete Todo Backend (Node.js HTTP Module)

Now Let's Create a basic Todo backend server that can handle different HTTP methods and routes.

Requirements

1. POST /create/todo

  • Read JSON body containing:

    • title

    • description

  • Create a new Todo

  • Assign an auto-incremented ID (starting from 1)

  • Return the updated list of all todos

2. GET /todos

  • Return all todos

  • Response should be in JSON format

3. GET /todo?id=XXX

  • Return a single todo based on ID

  • If todo does not exist:

    • Return 404

    • Response:

{ "error": "Todo not found" }

4. DELETE /todo?id=XXX

  • Delete a todo based on ID

  • If successful:

    • Return 200
  • If ID does not exist:

    • Return 404

    • Response:

{ "error": "Todo not found" }

📌 Full Implementation

const http = require("http");
const url = require("url");

let todos = [];
let currentId = 1;

const server = http.createServer((req, res) => {
  const urlData = url.parse(req.url, true);
  const { pathname, query } = urlData;

  // Helper to send JSON response
  const sendJSON = (status, data) => {
    res.statusCode = status;
    res.setHeader("Content-Type", "application/json");
    res.end(JSON.stringify(data));
  };

  // Root Route
  if (pathname === "/" && req.method === "GET") {
    res.statusCode = 200;
    res.setHeader("Content-Type", "text/plain");
    return res.end("Hello World");
  }

  // Create Todo
  if (pathname === "/create/todo" && req.method === "POST") {
    const bodyStream = [];

    req.on("data", (chunk) => {
      bodyStream.push(chunk);
    });

    req.on("end", () => {
      let body;

      try {
        body = JSON.parse(Buffer.concat(bodyStream).toString());
      } catch {
        return sendJSON(400, { error: "Invalid JSON" });
      }

      const newTodo = {
        id: currentId++,
        title: body.title,
        description: body.description,
      };

      todos.push(newTodo);

      return sendJSON(200, todos);
    });

    return;
  }

  // Get All Todos
  if (pathname === "/todos" && req.method === "GET") {
    return sendJSON(200, todos);
  }

  // Get Single Todo
  if (pathname === "/todo" && req.method === "GET") {
    const id = parseInt(query.id);

    if (!id) {
      return sendJSON(404, { error: "Todo not found" });
    }

    const todo = todos.find((t) => t.id === id);

    if (!todo) {
      return sendJSON(404, { error: "Todo not found" });
    }

    return sendJSON(200, todo);
  }

  // Delete Todo
  if (pathname === "/todo" && req.method === "DELETE") {
    const id = parseInt(query.id);

    if (!id) {
      return sendJSON(404, { error: "Todo not found" });
    }

    const index = todos.findIndex((t) => t.id === id);

    if (index === -1) {
      return sendJSON(404, { error: "Todo not found" });
    }

    todos.splice(index, 1);

    return sendJSON(200, { message: "Todo deleted" });
  }

  // Handle Unknown Routes
  res.statusCode = 404;
  res.setHeader("Content-Type", "application/json");
  res.end(JSON.stringify({ error: "Route not found" }));
});

server.listen(3000, () => {
  console.log("Server running on port 3000");
});

Final Thoughts

Understanding HTTP at this level helps developers understand how frameworks like Express actually work internally.

By building a server using the core http module, we gain a deeper understanding of:

  • Request streams

  • Response handling

  • Routing

  • HTTP methods

Once you understand this, frameworks like Express become much easier to work with.

More from this blog

Shubham Tech. Blog's

55 posts

Problem Solver | Currently Working As a Full Stack Developer, Community Leader At @Dev_Matrix | Previously Contributor at @RealDevSquad, @TeamShiksha