Skip to main content

app.HTTP_METHOD

Introduction: Handling Client Requests 📨

When users interact with your website (clicking buttons, submitting forms, loading pages), they send HTTP requests to your server. Express gives you methods to handle each type of request!

Real-World Analogy: Restaurant Orders 🍽️

┌─────────────────────────────────────┐
│ HTTP Method = Type of Request │
├─────────────────────────────────────┤
│ GET → "Show me the menu" │
│ POST → "Place my food order" │
│ PUT → "Replace my entire order"│
│ PATCH → "Change just the drink" │
│ DELETE → "Cancel my order" │
└─────────────────────────────────────┘

Each HTTP method corresponds to a different action!

The 5 Main HTTP Methods (CRUD Operations)

HTTP MethodPurposeCRUDReal Example
GETRetrieve dataReadView user profile
POSTCreate new dataCreateRegister new user
PUTReplace entire resourceUpdate (full)Replace user profile
PATCHUpdate parts of resourceUpdate (partial)Change user email
DELETERemove dataDeleteDelete user account

CRUD = Create, Read, Update, Delete (the 4 basic operations on data)

Visual: Request Flow

Client Server
│ │
│──── GET /users ──────>│ "Give me users list"
│<──── 200 [users] ─────│
│ │
│─── POST /users ──────>│ "Create new user"
│<──── 201 {user} ──────│
│ │
│─ DELETE /users/5 ────>│ "Delete user #5"
│<──── 204 No Content ──│

Understanding app.HTTP_METHOD() 🔧

Format

app.HTTP_METHOD() is NOT the actual method name!

Replace HTTP_METHOD with the specific HTTP method:

  • app.get() for GET requests
  • app.post() for POST requests
  • app.put() for PUT requests
  • app.patch() for PATCH requests
  • app.delete() for DELETE requests

Complete CRUD Example: Managing Users

User Management API
// READ - Get all users
app.get("/users", (req, res) => {
res.json({ users: ["Alice", "Bob", "Charlie"] });
});

// READ - Get specific user
app.get("/users/:id", (req, res) => {
const userId = req.params.id; // :id from URL
res.json({ id: userId, name: "Alice" });
});

// CREATE - Add new user
app.post("/users", (req, res) => {
const newUser = req.body; // Data from request
res.status(201).json({ message: "User created!", user: newUser });
});

// UPDATE (full) - Replace entire user
app.put("/users/:id", (req, res) => {
const userId = req.params.id;
const userData = req.body;
res.json({ message: `User ${userId} replaced`, user: userData });
});

// UPDATE (partial) - Change specific fields
app.patch("/users/:id", (req, res) => {
const userId = req.params.id;
const updates = req.body; // Only changed fields
res.json({ message: `User ${userId} updated`, changes: updates });
});

// DELETE - Remove user
app.delete("/users/:id", (req, res) => {
const userId = req.params.id;
res.json({ message: `User ${userId} deleted` });
});

What's happening:

GET /users → List all users
GET /users/123 → Get user #123
POST /users → Create new user
PUT /users/123 → Replace user #123 completely
PATCH /users/123 → Update user #123 partially
DELETE /users/123 → Delete user #123

:id parameter (123)
tip
URL Parameters (:id)

The :id in /users/:id is a placeholder:

app.get("/users/:id", (req, res) => {
console.log(req.params.id); // Access the ID
});

// URL: /users/42 → req.params.id = "42"
// URL: /users/999 → req.params.id = "999"
// URL: /users/alice → req.params.id = "alice"

You can name it anything: :userId, :userID, :username

Method Syntax Breakdown 📐

app.get(path, callback);
↑ ↑ ↑
Method Path Handler

Template:

app.METHOD(path, (req, res) => {
// Handle the request
// Send a response
});

Two required parameters:

  1. path - The URL route (e.g., /users, /api/books/:id)
  2. callback - Function to handle the request

Visual Example:

app.post("/api/users", (req, res) => {
↑ ↑ ↑ ↑
Method Path Request Response
Object Object
// 1. Get data from request
const userData = req.body;

// 2. Process data (save to database, etc.)

// 3. Send response
res.status(201).json({ message: "User created!" });
});

1. path

The first parameter is the URL for this function to act upon. In this case, we are targeting /, which is the root of our website: in this case, localhost:5000. It can be any URL. For example, you can use /about or /contact or /api/v1/users, etc.

caution

The combination of HTTP method (GET, POST, PUT, etc) and path must be unique. For example, if you define a GET request for /users and a POST request for /users. These are two different requests, because they have the same path but different HTTP methods. So, if you call the GET request for /users, then the POST request will not be called. If you call the POST request for /users then the POST request will be called.

But if you define two requests for the same HTTP method and the same path, then the first request will be called. For example:

Two requests for the same HTTP method and same path
app.get("/users", (req, res) => {
res.send("first");
});

app.get("/users", (req, res) => {
res.send("second");
});

See, both requests are for the same HTTP method (GET) and the same path (/users). So, the first request will be called, and response will be first. The second request will not be called.

So, be careful when you name your endpoints. Make sure that you are not using the same endpoint for different HTTP methods.

2. callback function

What is a callback function?

A callback function is a function that is passed as an argument to another function. If you are not familiar with callback functions, I would suggest you read this doc.

The second parameter is a callback function. It will be called when a request is made to the specified API endpoint. The callback function takes two parameters:

Callback function
(req, res) => {
// code goes here
};

The callback function takes two parameters:

  1. req (Request Object)
  2. res (Response Object)
  3. next (Next Function)

1. req (Request Object)

The req represents the request that was sent to the server. We can use this object to read data about what the client is requesting to do. There are several ways to send data in an API call. Some of the most common methods include:

Let's first see them

Params

Params are the values that are passed in the URL. It is a part of the URL path, and also known as path parameters (or path params). For example, you have a URL like http://localhost:5000/users/1, if you want to get the value that is after /user/, first you need to define a route like the one below.

app.get("/users/:id", (req, res) => {
// :id is the param
const id = req.params.id;
console.log(id); // 1
// ...
});

Params are defined in the URL path using a colon : followed by a name. For example, in the URL path /users/:id, the :id is a param. You can access the value of this param in the request handling function using req.params.id.

Normally developers use params to access or perform an action on a specific resource. That's why you would see params used in the URL path for GET, PUT, PATCH, and DELETE requests where you are accessing or modifying a specific resource based on its ID.

Query Parameters

Query parameters are optional values that can be appended to the end of the URL after a ? symbol, and can pass multiple query params by separating them using &. For example:

Example of query params
http://localhost:5000/users?sort=desc&limit=10&name=John

In this example, the query parameters are sort=desc, limit=10, and name=John. We can access the values using the req.query, for example:

app.get("/users", (req, res) => {
const sort = req.query.sort;
const limit = req.query.limit;
const name = req.query.name;
console.log(sort, limit, name); // desc 10 John
console.log(req.query); // { sort: 'desc', limit: '10', name: 'John' }
// ...
});
caution

You don't need to change the existing endpoint's path or create a new endpoint to pass and get query params, for example, GET http://localhost:5000/users?sort=desc&limit=10, and GET http://localhost:5000/users will hit the same API.

If you don't send query parameters in a request, the values in the req.query object will be an empty object {}. You can still access the properties of this object, but they will be undefined. For example, if you send a request to http://localhost:5000/users without query parameters, the output in the console will be:

app.get("/users", (req, res) => {
const sort = req.query.sort;
const limit = req.query.limit;
console.log(sort, limit); // undefined undefined
console.log(req.query); // {}
// ...
});

It is a good practice to check for the presence of query parameters before using their values in your application logic and to provide default values if the query parameters are not present. For example:

app.get("/users", (req, res) => {
const sort = req.query.sort || "asc";
const limit = req.query.limit || 10;
console.log(sort, limit);
// ...
});
tip

|| is or operator, and you can use it to provide a default value for a variable or expression.

The || operator returns the first operand if it is true, otherwise, it returns the second operand. So, you can use it like this:

Using || operator to provide a default value
const someValue = null;
const defaultValue = "Hello World";
let myVar = someValue || defaultValue;
console.log(myVar); // Hello World

For more information on operators like ||, &&, !, ==, ===, !=, !==, >, <, etc see this, if you haven't already.

In this example, default values are assigned to the sort and limit variables if the query parameters are not present in the request. This can help prevent errors in your application if the values are not present.

Query parameters are sometimes used to filter the data that is returned from the server, so instead of writing a new endpoint, you can use the same endpoint and send query parameters to filter the data like id=1, name=Rizwan and email=mrizwanashiq@outlook.com etc, then the URL will be like http://localhost:5000/users?id=1&name=Rizwan&email=mrizwanashiq@outlook.com

You can also send an array of values using query parameters. For example, consider the following URL:

http://localhost:5000/users?name=rizwan&age=19&name=ashiq

In this example, the sort query parameter is sent twice. The req.query object will contain an array of values for the name query parameter:

app.get("/users", (req, res) => {
const name = req.query.name;
console.log(name); // [ 'rizwan', 'ashiq' ]
// ...
});

You can also send an array like this:

Sending an array of values
http://localhost:5000/users?name=rizwan,ashiq

You will also get an array against the name query parameter in the req.query object.

Objects can also be sent as query parameters. For example, consider the following URL:

Sending an object as a query parameter
http://localhost:5000/users?name=rizwan&age=19&address[city]=karachi&address[country]=pakistan

In this example, the address query parameter is an object. The req.query object will contain an object for the address query parameter, and the city and country properties will be available as properties of the address object like req.query.address.city and req.query.address.country. The output of req.query.address in the console will be:

Output of req.query.address
{ city: 'Karachi', country: 'Pakistan' }

Headers

Headers are the key-value pairs that are sent in the request, no matter what the request type is like GET, POST, PUT, DELETE, etc. For example, if you have a URL like http://localhost:5000/users, you want to get the value of Content-Type, or let's say authorization header, first you need to define a route like the one below.

Getting the value of authorization header
app.get("/users", (req, res) => {
const authorization = req.headers.authorization;
console.log(authorization);
// ...
});

req.headers is an object, so you can access the value of any header using req.headers["headerName"] or req.headers.headerName, but if the header name has a dash in it, then you can only access it like req.headers["header-name"] not req.headers.header-name because if the key has a hyphen (-) in it, then it will be considered as req.headers.header - name, not a key with a hyphen in it.

But, if you want to access the value of content-type header, you can use req.headers["content-type"] not req.headers.content-type

Headers are case-insensitive

Maybe it looks weird, but it's true.

Header keys are case-insensitive. So, you can use req.headers["Content-Type"] or req.headers["CONTENT-TYPE"] or req.headers["content-type"] etc.

Body

Data can be sent in the body of the request using various formats such as JSON, XML, or form data. This method is commonly used for sending larger amounts of data or more complex data structures. For example, if you want to send a JSON object in the request body, you need to set the Content-Type header to application/json. The important thing to note is that not all the requests have a body, for example, GET requests don't have a body. So which requests have a body? POST, PUT, PATCH, DELETE, etc. requests have a body.

How to get the body? Express has a built-in middleware to parse JSON, URL-encoded, and text data. For example, you send a POST request to http://localhost:5000/users with the following body:

Request Body
{
"name": "Rizwan",
"email": "mrizwanashiq@outlook.com"
}

You can get the body using req.body like this:

import express from "express";

const app = express();

app.post("/users", express.json(), (req, res) => {
console.log(req.body);
// {
// "name": "Rizwan",
// "email": "mrizwanashiq@outlook.com"
// }
});

Here, I used express.json() as a middleware to parse the body, it will parse the body, and place it in req.body. You can also use express.urlencoded() if the body is form data. And you can also use both of them together like this:

app.post("/users", express.json(), express.urlencoded(), (req, res) => {
// ...
});

But, it's not a good practice to add body-parser middleware to every route, so you can use app.use() to add it to all the routes. For example, if you want to parse JSON and form data bodies, you can use the following code:

import express from "express";

const app = express();

app.use(express.json()); // to support JSON-encoded bodies
app.use(express.urlencoded({ extended: true })); // to support URL-encoded bodies

app.post("/users", (req, res) => {
console.log(req.body);
// {
// "name": "Rizwan",
// "email": "mrizwanashiq@outlook.com"
// }
});

app.patch("/users/:id", (req, res) => {
console.log(req.params.id); // 1
console.log(req.body);
// {
// "name": "Rizwan",
// "email": "mrizwanashiq@outlook.com"
// }
});

app.post("/books", (req, res) => {
console.log(req.body);
// {
// "title": "The Alchemist",
// "author": "Paulo Coelho"
// }
});

Here, I added express.json() and express.urlencoded() as a global middleware, so now you can get the body using req.body in every route without adding them on every route the way I did in the above example.

tip
body-parser

Over the years, body-parser has been very popular for the parsing body, but now express has built-in middleware for the parsing body, so you can use them instead of body-parser. body-parser is a third-party middleware, so you need to install it using npm or yarn:

npm install body-parser

Then you can use it like this:

import express from "express";
import bodyParser from "body-parser";

const app = express();

app.use(bodyParser.json()); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // to support URL-encoded bodies

app.post("/users", (req, res) => {
console.log(req.body);
// {
// "name": "Rizwan",
// "email": "mrizwanashiq@outlook.com"
// }
});

But, I recommend you to use the built-in middlewares instead of body-parser because body-parser is deprecated now.

Miscellaneous

Other than params, query, headers, and body, you can also get the request's method, protocol, hostname and plenty of other useful data.

app.get("/users", (req, res) => {
console.log(req.method); // GET
console.log(req.protocol); // http
console.log(req.hostname); // localhost
console.log(req.ip); // ::1
console.log(req.path); // /users
console.log(req.originalUrl); // /users
console.log(req.baseUrl); // ""
console.log(req.subdomains); // []
console.log(req.accepts("json")); // true
console.log(req.accepts("application/json")); // true
console.log(req.accepts("application/xml")); // false
console.log(req.accepts("text/plain")); // true
console.log(req.accepts("image/png")); // false
console.log(req.accepts("*/*")); // true
console.log(req.accepts()); // true
console.log(req.is("json")); // false
console.log(req.is("application/json")); // false
console.log(req.is("application/xml")); // false
// etc.
});

2. res (Response Object)

The res represents the response that will be sent to the client. For example, you can send a JSON object, HTML, text, etc.

res.json({ name: "John" }); // Content-Type: application/json
res.send({ name: "John" }); // Content-Type: application/json
res.send("<h1>Hello World</h1>"); // Content-Type: text/html
res.send("Hello World"); // Content-Type: text/html
res.status(200).send("OK"); // Content-Type: text/html
res.status(404).json({ error: "Not Found" }); // Content-Type: application/json
res.status(500).json({ error: "Internal Server Error" }); // Content-Type: application/json

These are the most common methods that you will use to send data to the client.

  1. res.send()
  2. res.json()
  3. res.status()

Let's see them one by one.

res.send()

res.send() is used to send data to the client. It can send a JSON object, HTML, text, etc. It will automatically set the Content-Type header based on the data you send.

res.send({ name: "John" }); // Content-Type: application/json
res.send("<h1>Hello World</h1>"); // Content-Type: text/html
res.send("Hello World"); // Content-Type: text/html

res.json()

res.json() is used to send a JSON object to the client. It's the same as res.send() but it sets the Content-Type header to application/json.

res.json({ name: "John" }); // Content-Type: application/json

res.status()

res.status() is used to set the status code of the response. For example, if you want to send a 404 status code, you can use res.status(404).json({ error: "Not Found" }). But if don't set the status code, it will be 200 by default.

res.status(200);

It's chainable, so you can use it like this:

res.status(404).json({ error: "Not Found" });
res.status(500).json({ error: "Internal Server Error" });

So, what is the status code?

What is Status Code?

The status code is a number that represents the status of the response. For example, 200 means OK, 201 means Created, 400 means Bad Request, 401 means Unauthorized, 403 means Forbidden, 404 means Not Found, 500 means Internal Server Error, etc.

It's a good practice to set the status code of the response so that the client knows what happened. Every status code has a meaning, for example, 200 means OK, 201 means Created, 400 means Bad Request, 401 means Unauthorized, 403 means Forbidden, 404 means Not Found, 500 means Internal Server Error, etc.

3. next (Next Function)

The next function is used to call the next middleware or controller. So first of all, what is middleware, and see how it uses the next function.

What is Middleware?

Middleware in Express.js refers to a series of functions that are executed in the request-response cycle of an Express application. These functions have access to the request and response objects, and they can modify or augment them in various ways.

Middleware functions can be used for a variety of purposes, such as handling authentication, logging, error handling, parsing request data, and many others. They can be defined at the application level, the router level, or the route level.

When a client requests an Express application, the request is passed through a series of middleware functions before it reaches the final route handler. Each middleware function has the option to modify the request or response objects and to pass control to the next middleware function in the chain.

How it use the next function?

Middleware functions are defined using the use() method of an Express application, router, or route object. Here's an example of a middleware function that logs the timestamp and URL of each request:

app.use((req, res, next) => {
console.log(`[${new Date()}] ${req.method} ${req.url}`);
next();
});

In this example, the use() method is used to define a middleware function that logs the timestamp and URL of each request. The function takes three parameters: the req object, the res object, and the next function. The next function is used to pass control to the next middleware function in the chain.

Middleware functions are a powerful feature of Express.js that allows developers to modularize their code and add additional functionality to their applications.

Here, next is a function that will call the next middleware or controller. If you don't call next(), then the request will be stuck in the current middleware.

You can create as many middleware as you want. For example, if you want to create three middlewares, then you can do it like this:

app.use((req, res, next) => {
console.log("First Middleware");
next();
});

app.use((req, res, next) => {
console.log("Second Middleware");
next();
});

app.use((req, res, next) => {
console.log("Third Middleware");
next();
});

app.get("/users", (req, res) => {
res.send("Hello World");
});

In the above example, if you make a GET request to /users, then the output on the console will be:

Output on the console
First Middleware
Second Middleware
Third Middleware

If you don't call next(), then the output will be

Output on the console
First Middleware

The request will be stuck in the first middleware, and the response will never be sent to the client.

Above all the middleware is global middleware. If you want to create a middleware that will be called only for a specific API, then you can do it like this:

API with a middleware
// First create a middleware
const myMiddleware = (req, res, next) => {
console.log("My Middleware");
next();
};

app.get("/users", myMiddleware, (req, res) => {
res.send("Hello World");
});

In the above example, the myMiddleware will be called only for the GET request to /users.

Controllers

Controllers are the functions that handle the request and send the response. For example, you can use controllers to get the data from the database, and then send the data to the client.

Controller
app.get("/users", (req, res) => {
res.send("Hello World");
});

In the above example, the GET request to /users is handled by the controller function. The controller function gets the request and response objects as parameters, and then it can do anything with the request and response objects.

Conclusion

In this article, we learned about the request and response objects in Express. We learned about the req object, and how to get the data from the request. We also learned about the res object, and how to send the data to the client. We also learned about the next function, and how to call the next middleware in the chain. We also learned about the controllers, and how to handle the request and send the response.