Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine two middleware and export as one?

I have a utility file which basically has two functions (one to detect user location and another to get user device details) acting as middlewares. Now I would like to know whether it is possible to combine both the middlewares together as one so that it could be used with other middlewares on the route. I would also like to have the freedom of using the functions in the utility file individually when required.

Utility file

const axios             = require("axios");
const requestIp         = require("request-ip");

const getDeviceLocation = async (req, res, next) => {
    try {
        // ToDO: Check if it works on production
        const clientIp  = requestIp.getClientIp(req);
        const ipToCheck = clientIp === "::1" || "127.0.0.1" ? "" : clientIp;

        const details = await axios.get("https://geoip-db.com/json/" + ipToCheck);

        // Attach returned results to the request body
        req.body.country    = details.data.country_name;
        req.body.state      = details.data.state;
        req.body.city       = details.data.city;

        // Run next middleware
        next();
    }
    catch(error) {
        return res.status(500).json({ message: "ERROR_OCCURRED" });
    }
};

const getDeviceClient = async (req, res, next) => {
    const userAgent = req.headers["user-agent"];

    console.log("Device UA: " + userAgent);
    next();
};

module.exports = { getDeviceLocation, getDeviceClient };

Example Routes

app.post("/v1/register", [getDeviceLocation, getDeviceClient, Otp.verify], User.create);

app.post("/v1/auth/google", [getDeviceLocation, getDeviceClient, Auth.verifyGoogleIdToken], Auth.useGoogle);  

I would like to have getDeviceLocation and getDeviceClient combined into one say getDeviceInfo yet have the freedom to use getDeviceLocation and getDeviceClient individually when required on any route.

like image 609
Ayan Avatar asked Jun 01 '19 16:06

Ayan


People also ask

What is middleware in middleware?

Middleware has the ability to modify the req and res objects, run any code you wish, end the request and response cycle, and move onto the following functions. Note the order of your middleware, as invoking the next () function is required in each preceding middleware.

How do I get the argument of a middleware function?

All middleware functions in Express.js accept three arguments following the request, response, and next lifecycle methods. In your index.js file, define a function with the three lifecycle methods as arguments: function myCustomMiddleware( req, res, next) { // ... }

How to create a middleware for saving?

Or we can create a middleware for saving, which will do the following: Whenever a save request arrives, call Product/User server's save and Search server's save. If the first save fails, do not call the save on other one (this keeps the databases consistent). Let's look at the design diagrams without and with a middleware.

How does middleware speed up the development of distributed applications?

Middleware speeds development of distributed applications by simplifying connectivity between applications, application components and back-end data sources. What is middleware? Middleware is software that enables one or more kinds of communication or connectivity between two or more applications or application components in a distributed network.


3 Answers

Express allows you to declare middleware in an array, so you can simply define an array of the middlewares you'd like to combine:

const getDeviceLocation = async (req, res, next) => {
...
};

const getDeviceClient = async (req, res, next) => {
...
};

const getDeviceInfo = [getDeviceLocation, getDeviceClient];

module.exports = { getDeviceLocation, getDeviceClient, getDeviceInfo };

You can then use any combination of one or both of the middleware wherever you like:

app.use('/foo', getDeviceLocation, () => {});
app.use('/bar', getDeviceClient, () => {});
app.use('/baz', getDeviceInfo, () => {});
like image 140
Myk Willis Avatar answered Oct 25 '22 23:10

Myk Willis


In your case maybe you can use something simple like this

const getDeviceInfo = async (req, res, next) => {
    await getDeviceClient(req, res, async () => {
        await getDeviceLocation(req, res, next)
    })
}

But you may need to handle error cases.

like image 26
Rami Jarrar Avatar answered Oct 26 '22 00:10

Rami Jarrar


If you'd like to avoid callback hells you can use something like this:

const nextInterceptor = error => {
  if (error) { throw error; }
};

const combineMiddlewares = middlewares => (req, res, next) => (
  middlewares.reduce((previousMiddleware, currentMiddleware) => (
    previousMiddleware.then(() => currentMiddleware(req, res, nextInterceptor))
  ), Promise.resolve())
    .then(() => next())
    .catch(next)
);

const commonMiddlewares = combineMiddlewares([getDeviceLocation, getDeviceClient]);

app.post("/v1/register", [commonMiddlewares, Otp.verify], User.create);
app.post("/v1/auth/google", [commonMiddlewares, Auth.verifyGoogleIdToken], Auth.useGoogle);
like image 1
csidro Avatar answered Oct 26 '22 00:10

csidro