6

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.

Ayan
  • 2,738
  • 3
  • 35
  • 76

5 Answers5

5

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, () => {});
Myk Willis
  • 12,306
  • 4
  • 45
  • 62
1

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.

Rami Jarrar
  • 4,523
  • 7
  • 36
  • 52
1

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);
csidro
  • 11
  • 1
0

Using connect nodemodule you can do combine the middle ware and expose a new end point for it. Ref here a sample code https://blog.budiharso.info/2015/07/28/Combine-multiple-express-middleware/

Senthil
  • 2,156
  • 1
  • 14
  • 19
  • 1
    Yes, I have gone through it before asking here but it asks for connect package. Is there any other alternative way of doing? – Ayan Jun 01 '19 at 16:25
  • In the code which you posted you are following the chaining multiple middle ware calls without using any third party module Ref: https://stackoverflow.com/questions/31928417/chaining-multiple-pieces-of-middleware-for-specific-route-in-expressjs – Senthil Jun 01 '19 at 16:34
  • If you still want to expose a different route, then using node modules 3rd party like combine, compose-middleware, middleware-flow are the other options. – Senthil Jun 01 '19 at 16:35
  • Which one do you suppose is better. I haven't used any of them. – Ayan Jun 01 '19 at 16:37
  • combine is better – Senthil Jun 01 '19 at 16:49
0

It's simple and doesn't require to install anything. Just use:

const {Router} = require('express')

const combinedMiddleware = Router().use([middleware1, middleware2, middleware3])

Now you can use combined middleware where necessary. For example:

app.get('/some-route', (req, res, next) => {
  req.query.someParam === 'someValue'
    ? combinedMiddleware1(req, res, next)
    : combinedMiddleware2(req, res, next)
})
plashenkov
  • 101
  • 2
  • 3