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

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→ protocollocalhost: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:
statusCodetells 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
chunkvariable contains a small piece of the request bodyWe store each chunk inside the
bodyStreamarray
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:
Listen for incoming data chunks
Store them in a container
Detect when all data has arrived
Combine the chunks
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:
titledescription
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
404Response:
{ "error": "Todo not found" }
4. DELETE /todo?id=XXX
Delete a todo based on ID
If successful:
- Return
200
- Return
If ID does not exist:
Return
404Response:
{ "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.




