What is Middleware in API?

Angelo YANVEAngelo YANVE
5 min read

Hello there!
My name is Angelo YANVE, I'm Software Engineer specifically Backend Engineer. In this article, We are going to learn about Middleware and we will browse some example using ExpressJs.

What is API?

API stands for Application Programming Interface is a mechanism that allows two software to communicate between each other using a set of definition protocols with Request and Response.

What is Middleware?

Middleware is an interface between Client Request and Server Response. It works like some features that can be used to handle HTTP requests and responses, process data, perform validations, and implements other specific functionality.

Middleware use cases

Here are some common roles that middleware can play in API

  • Authentication and Authorization: middleware can be used to verify user's identity and determine if they have the necessary permission to access the resources they need

  • Data Validation: middleware can performs checks on data received in requests ensuring that it is correct and complies with specified requirements before being processed by the API

  • Caching: Some middleware can be used to cache query to improve performance by avoiding redundant processing of similar requests.

  • Data Tansformation: middleware can transform data before or after processing by the API, for example by converting data formats, adding information etc...

Why use Middleware?

Now we are going to explore the essential reasons why we need to use middleware in API development. Middleware offers benefits such as :

  • Code simplification: Middleware help to separate business logic from common logic like authentication, data validation, error handling, etc...

  • Reusability: Middleware functionality can be reused in different parts of our application or between many applications by modularity and efficiency

  • Centralize management: Middleware allow you to centralize certain functionality in one place, then make maintenance and debugging easier

  • Responsibilities separation: Middleware encourages separation of responsibilities by helping them to focus on their main task. which allows adding new features or updating existing ones without changing business logic in the entire application

ExpressJS

ExpressJS is a most popular NodeJS framework that allows developers to build server side applications using JavaScript.

Middlewares in ExpressJS are functions that are executed during the lifecycle of a request to the ExpressJS server. These functions have access to the request object, response object, and a next function (which, when called, proceeds to complete the request). They can be used to modify the request and response objects.

Now we will to take the example of a Get request to the ExpressJS server to get all the post from a private forum and we need to be authenticated and validated by the admin to get all post

app.get('/posts',(req, res, next)=>{
    // Check to see if the request has Bearer Token
    const authorizationHeader = req.headers['authorization'];
    if (!authorizationHeader || !authorizationHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Unauthorized' });
    }
    const token = authorizationHeader.split(' ')[1];

    //Get user account and and check if validate
    getAccount(token, (account)=>{
        if(!account) return res.status(401).json({ msg: 'User Account not found' });
        if(!account.approved) return res.status(403).json({msg: 'Your account not approved, please check your mails'});
    });

    // The account is valid and approved, grab the posts and send them back
    db.getPosts((allPosts)=>{
        if (!allPosts) {
            return res.status(404).json({ msg: 'post not found' });
        } 
        return res.status(200).json({posts: allPosts });
    });

});

This above code looks good, it performs the necessary process we define to get all forum posts. However, a significant portion of this code may be reused elsewhere. For example if we need to get all comments for a post, we will authenticate the user and check if they are approved.

We could then consider splitting the code into smaller function and use it as Middleware

The request chain goes roughly like this:

  1. Check is request has Bearer token, if not, response "Unauthorized" early

  2. Check if user account is "Approved", if not, response "Not Approved"

  3. Get the posts from the database and return them to the user.

If we split this out into smaller middleware functions, it might look like this.

function getUserToken(req, res, next){
    const authorizationHeader = req.headers['authorization'];
    if (!authorizationHeader || !authorizationHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Unauthorized' });
    }
    req.user.token = authorizationHeader.split(' ')[1];
    next();
}
function checkUserApproved(req, res, next){
    getAccount(token, (account)=>{
        if(!account) return res.status(401).json({ msg: 'User Account not found' });
        if(!account.approved) return res.status(403).json({msg: 'Your account not approved, please check your mails'});
    });
    next();
}
app.get('/posts',getUserToken , checkUserApproved, (req, res, next)=>{
    db.getPosts((allPosts)=>{
        if (!allPosts) {
            return res.status(404).json({ msg: 'post not found' });
        }    
        return res.status(200).json({posts: allPosts });
    });
});

So now we have middleware for authentication and check if account is approved, which can be used in another request, like getting all comments from a post.

We can also load middleware by using app.use()

// Apply getUserToken & checkUserApproved
app.use(getUserToken);
app.use(checkUserApproved);

app.get('/posts', (req, res, next)=>{
    db.getPosts((allPosts)=>{
        if (!allPosts) {
            return res.status(404).json({ msg: 'post not found' });
        }    
        return res.status(200).json({posts: allPosts });
    });
});

Before concluded let's explain next()

What is next()?

Middleware function get 3 parameter req, res which respectively access to Request and Response and next which is used to move to the next middleware or function if the current middleware function does not complete the request-response cycle

For example when we load this get request

app.get('/', authenticate, log_data, update_req);

Middlewares functions are called in the order authenticate() -> log_data() -> update_req(). This order will be interrupted if the authenticate and log_data functions don't have next() function to move on following function

The next function can be named otherwise too but this is the convention followed to avoid any confusion.

💡
Using next() in the last function update_req is optional and won’t do anything since it is the last function in the request-response cycle.

Conclusion

Middleware is a great tool for organizing your code in the Request-Response cycle. It's a function with access to the request req and response res before and after current request is dealt with.

Hope this article helps you!!
Leave a comment please.

30
Subscribe to my newsletter

Read articles from Angelo YANVE directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Angelo YANVE
Angelo YANVE