An Express app is largely composed of middleware function calls that enable developers to quickly build web applications.
In this article, we will learn what middleware is, the importance of middleware, and how to write your own middleware for ExpressJS applications.
By the end of the article, you should know :
What middleware is
The Importance of middleware
The built-in Express middleware functions
How to create and use middleware function in our Express app.
Prerequisites:
Before getting started, you should have knowledge on:
What ExpressJS is
Introduction to Middleware
Whenever an Express app receives a request, it needs to send back a response. A middleware component sits between a request and a response.
It is those methods/functions/operations that are called between processing the request and sending the response in an Express app.
Middleware executes whenever the server receives a request and has access to the request object (req
), the response object(res
), and a next()
function.
How middleware works
Consider an analogy, where you are a property owner with tons of apartments to let out to tenants. However, you are selective about the type of tenant that occupies the apartment. You want tenants with specific social classes and ethnicities.
To schedule a viewing, potential clients must call you.
To avoid the headache of screening all potential tenants, you hire a real estate agent to oversee these activities. Whenever a potential tenant requests an apartment, the agent will schedule an initial meeting with the client. The agent screens a client to determine if he meets your criteria.
The agent informs you to allow the client to view the apartment once he meets all the requirements. You then pay the agent a commission for his service.
In this analogy:
The tenant represents the HTTP request performed by the client
The real estate agent represents the middleware
The property owner represents the server
The agent (middleware) ensures any request by a client to access an apartment meets the criteria determined by the property owner ( server).
In effect, the middleware (i.e the real estate agent) sits between the client (i.e tenant) and the server(i.e property owner ) and determines the next action to take.
Should the client not meet the criteria, the middleware has the right to terminate the request. In effect, ending the request-response cycle.
Importance of middleware
Middleware functions can perform the following tasks:
Add logging and authentication functionality to our application
Can be used to manipulate requests
Make changes to the request and response objects
Call the next middleware function in the stack
Can process request objects multiple times before the server works on the request.
It improves client-side rendering performance.
Types of middleware functions
An Express app can use the following types of middleware:
Application-level middleware: a type of middleware that generally runs throughout the whole application
Router-level middleware: router-level middleware work just like application-level middleware except they are bound to an instance of express. Router()
Error-handling middleware: the purpose of error-handling middleware is to detect and capture multiple error conditions and take corrective actions to recover from or gracefully fail in the face of those errors.
Built-in middleware: middleware built into the Express application.
Third-party middleware: third-party packages available that provide the functionality that our app requires.
We will discuss these types as we progress.
Setting up the ExpressJS development environment
To demonstrate how middleware works, we will set up a basic ExpressJS application.
Follow these steps to set up an Express app
Open your favorite code editor and create a project directory.
Install the
package.json
fileInstall the
express
library in the directory
The following commands illustrate the above:
mkdir middleware-tuts
cd middleware-tuts
npm init -y
npm install express
Create an index.js
file and add the code snippets below to create a basic express application.
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send(`<h1>Home</h1>`);
});
app.listen(5000, () => {
console.log("App has started and listening on port 5000");
});
You can also install nodemon
using the command below. This will automatically restart the server whenever it detects changes to the file.
npm install nodemon
Modify the scripts
in the package.json
file to the following:
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
Start the application using the command below:
npm run dev
Congratulations, you have created an Express app. Visit http://localhost:5000
to view your app.
Creating the middleware in an Express app
Because middleware is a function, we can use any preferred function declaration. We pass the function three parameters req
, res
and next
.
The syntax is as below:
const middleWareName = (req,res,next)=> {
//logic here
next() // call the next middleware
}
As mentioned, one of the main tasks of middleware is to manipulate requests.
In the example below, we create a basic logger
middleware that logs a message whenever the client requests a resource
//create the middleware
const logger = (req, res, next) => {
console.log(`Hello, you've hit our server`);
next();
};
Understanding the middleware parameters
Middleware accepts three parameters req
and res
objects and the next
function.
Manipulating the request object
The
req
is passed as a parameter during the function declaration.Because middleware enables us to manipulate requests, in the body of the function, we can access all the available methods in the
req
object.
In the example below, whenever we receive a request, we log the HTTP method
used and a message
//declare the logger middleware
const logger = (req, res, next) => {
console.log(req.method); // logs the http request
//log the below whenever the client reqest for a resouce
console.log(`Hello you've hit our server`);
next();
};
If the client is requesting data, the output will be:
GET
Hello you've hit our server
Manipulating the response object
The middleware can also manipulate the res
object.
In the example below, we use the res.send()
method to send back a response to the client.
//declare a logger middleware
const logger = (req, res, next) => {
console.log(req.method); //logs the http reqest method
//send a response to the client
res.send(`<h1>Middleware responded</h1>`);
};
Whenever you use the res
object in the function's body, it will not allow any request to go past it.
In effect, any other logic we may have implemented in the app will not be accessed.
Accessing the next() function.
The next()
is a function that enables us to invoke the next middleware in the middleware stack.
Whenever the current middleware function does not end the request-response cycle, it must call next()
to pass control to the next middleware else the request will be left hanging (evidenced by the browser always reloading).
Below is how we pass the control to the next middleware
const logger = (req, res, next) => {
... // all other logic here
next() // call the next middleware
};
Using the middleware
Until now, we have learned what middleware is and the functions of all the parameters.
Let's now see how we use declared middleware.
The syntax below illustrates how to use a middleware function
app.use([path,] callback [, callback...])
The
app.use()
connects the declared middleware function(s) to our application at the specified path.The
path
represents the route for which the middleware function is invoked.The
callback
represents a middleware function, a series of middleware functions (separated by commas), or an array of middleware functions that will be executed.
The code snippet below is how we will use the logger
middleware function
//using the logger middleware
app.use("/", logger);
In the example above, whenever the client visits the home route (/
) the logger
middleware is executed.
Middleware staged without a path
, for instance, app.use(middleware)
will be executed for every request to the app.
Middleware order of execution
Middleware is executed in the order in which they are added.
Consider the example below:
app.use(middleware1)
app.use(middleware2)
app.use(middleware1)
will be executed before app.use(middleware2)
Using multiple middlewares
In our app.use()
, we can declare an array of middleware functions that will be executed depending on the route requested.
In the code below, we declare the query
middleware in addition to the logger
middleware.
//declare the logger middleware
const logger = (req, res, next) => {
console.log(req.method); // logs the http request
//log the below whenever the client reqest for a resouce
console.log(`Hello you've hit our server`);
next();
};
//query middeleware
const query = (req, res, next) => {
console.log(req.query)
next();
};
The query
middleware also sits between the request-response cycle and can manipulate requests.
With this example, the req.query
object becomes populated with the query strings. These query strings are key-value pairs and start after the question mark in any URL.
For instance http://localhost:3000/dashboard/user?name=emmanuel&age=25
The output will be :
{ name: 'emmanuel', age: '25' }
Let's now use the declared middleware. The syntax below indicates how to use a series of middleware in our application
//array of middlewares
app.use([mddleware1, middleware2]);
We can use the logger
and query
middlewares like the below:
//use the middlewares
app.use([logger, query]);
Registering the path to execute the middleware
Depending on the http method
use, we can specify the path that all the declared middleware will be executed
The syntax is as below:
app.method(path, middleware, (req, res) => {
//do something here
});
Assuming, we want the query
middleware to be executed only when the clients request a resource on the path
: dashboard/user
, we define it as below:
app.get("/dashboard/user", query , (req, res) => {
//do something here
});
In effect, the query
middleware will only be invoked when the path specified in our app matches that of the client whiles the logger
middleware executes when the client requests a resource on the root /
.
The complete code for declaring and using middleware is as below:
const express = require("express");
const app = express();
const port = 3000;
//declare the logger middleware
const logger = (req, res, next) => {
console.log(req.method); // logs the http request
//log the below whenever the client reqest for a resouce
console.log(`Hello you've hit our server`);
next();
};
//declare the query middeleware
const query = (req, res, next) => {
console.log(req.query);
next();
};
//use the middlewares
app.use([logger, query]);
//logger middleware get executed here
app.get("/", (req, res) => {
res.send(`<h1>Hello World !</h1>`);
});
//use the query middleware
app.get("/dashboard/user", query, (req, res) => {
// access the name key of the query string
const { name } = req.query;
res.send(`<h1>Welcome ${name}</h1>`);
});
app.listen(port, () => {
console.log(`Example app is listening on port ${port}`);
});
Using Built-in middleware
Express has built-in middlewares that enable us to perform tasks.
Examples of these are:
express.static
express.json
express.urlencoded
express.raw
express.text
Using the express.static
middleware
The express.static
built-in middleware enables us to render static files such as images, CSS, and JavaScript files on the client-side
Unlike sending a file using the res.sendFile()
which permits serving only single files, the express.static()
allows all files in a specified folder to be available to the public.
The syntax is as below
express.static(root, [options])
The root
represents the root directory from which to serve static assets. If we want to render web pages, the directory should contain an index.html
or index.js
file which serves as the home page.
Assuming we created a public
folder containing all the required files in our project directory, we can serve these files using the code snippet below.
express.static('public') // built-in middleware
We can use this built-in middleware as below:
app.use(express.static('public'))
Using express.json
middleware
Express provides a middleware to handle incoming data in the request's body.
The express.json()
is a built-in middleware that enables us to recognize incoming request objects as JSON.
It enables us to send data to the server.
The syntax is as below:
express.json([options])
We can use this built-in middleware as
// parse json
app.use(express.json())
Using the express.urlencoded
middleware
The express.urlencoded()
enable us to identify incoming request objects as strings or arrays.
This method is called using the code:
app.use(express.urlencoded())
Third-party middleware
There are many third-party middleware packages available that provide the functionality that our app requires.
Below are some examples:
morgan
: anHTTP
request logger middlewarehelmet
: a middleware that improves the security of your application.compression
: a middleware that attempts to compress response bodies for all of the responses.
Summary
Middlewares are methods/functions/operations that are called between processing the request and sending the response in an Express app.
It sits between the request and response cycles on an Express app
It can be used to manipulate requests
It can also make changes to the request and response objects
This article is the day 7 post on the 30-day challenge I am undertaking to learn backend development. Please feel free, to drop some insights, comment, or share the post to your networks