Calling a middleware from within a middleware in NodeJS/ExpressJS - node.js

I have created some standard middleware with some logic, and depending on the logic I need to call some 3rd party middleware.
Middleware is added using app.use(), which is where I add my custom middleware.
Once in my middleware I no longer have access to app.use(), how do I call the middleware?
Here is some code:
Any ideas ?
const customerData = (req, res, next) => {
try {
console.log('Started');
if (process.env.STORE_CUSTOMER_DATA === 'true') {
// Here is some custom middleware that doesn't belong to me
//
// Returns a function (I confirmed it) ready to be called with res,req, next
//
let externalMiddlware = logger({
custom:true
});
// Do I return it ? Call it ? Trying everything and nothing seems to work
externalMiddlware(req,res,next); // ???
} else {
// DO not call external middleware, will break out of if and then call next()
}
console.log('Finished');
next();
} catch (err) {
next(err);
}
};
module.exports = customerData;

I think this should work but if you delegate the callback to this other externalMiddlware you should not call next() in customerData use this 3rd middleware
so have you try
const customerData = (req, res, next) => {
try {
console.log('Started');
if (process.env.STORE_CUSTOMER_DATA === 'true') {
let externalMiddlware = logger({
custom:true
});
return externalMiddlware(req,res,next);
} else {
return next(); // <= next moved
}
} catch (err) {
next(err);
}
};
module.exports = customerData;

Related

How to pass pg-promise transaction/task between ExpressJS middlewares?

ExpressJS can have a chain of middlewares for a single route, which at the very least allows to split input validation logic from the actual handling of the inputs.
The usual pattern looks like this:
import { Router } from "express";
const router = Router();
router.use("/entity/:id", async (req, res, next) => {
try {
// validation logic
const isValid = ...
// short circuit if validation fails
if (!isValid) {
throw new Error("error message")
}
// get to the next middleware in chain
next()
} catch (error) {
// pass the error to the error-handling middleware chain instead
next(error)
}
});
router.get("/entity/:id", async (req, res, next) => {
try {
// route handling logic
...
} catch (error) {
next(error)
}
});
This works fine as long as validation middleware doesn't require database calls. But in the most basic usecase, auth, it does.
So auth code looks like this:
import { Router } from "express";
// `db` object created as per instructions
// https://vitaly-t.github.io/pg-promise/index.html
import { db } from "#db"
const router = Router();
router.use("/auth", async (req, res, next) => {
try {
// key retrieval logic
// assume it throws on any mistake
const authKey = ...;
// retrieve session by key
const session = await db.one(
"...",
{ authKey }
)
// throw if it's not found for whatever reason
if (!session) {
throw new Error("error message")
}
// save the session value for the request duration
// so the middleware/handlers can access
res.locals.session = session
// get to the next middleware in chain
next()
} catch (error) {
// pass the error to the error-handling middleware chain instead
next(error)
}
});
router.get("/auth/:id/posts", async (req, res, next) => {
try {
const { id } = req.params;
// it is guarantied to be always there because
// of the middleware before
const session = res.locals.session;
const posts = await db.manyOrNone("...", {session, id})
return res.status(200).json(posts);
} catch (error) {
next(error)
}
});
The issue here is these 2 middlewares will use 2 separate connections, and it will only get worse with more complicated routing.
Is there a way to create a transaction/task object, which is an argument for the callback for db.tx()/db.task(), which could be passed around outside of these methods?

Express - Wrapping controller functions with try-catch

I have an express backend application. The problem I have is that all the routes contains the same try-catch piece which causes code bloat in my program:
// routes.js
router.get('/', async (req, res, next) => {
try {
const data = extractData(req)
await foo(data)
} catch (err) {
next(err)
}
})
// controllers.js
async function foo(data) {...do smh}
As you see above, try { extractData() } catch (err) { next(err) } portion of the code exists in all of the routes defined in the app.
I tried to create a wrapper function that takes controller function as parameter and use it as:
// routes.js
router.get('/', controllerWrapper(req, res, next, foo))
// controller-wrapper.js
async function controllerWrapper(req, res, next, controllerFunc) {
try {
const data = extractData(req)
await controllerFunc(data)
} catch (err) {
next(err)
}
}
But this does not work due to function being invoked, and not being actually a callback.
How can I achieve this?
You should use a closure for this, so you can return the middleware function from controllerWrapper and use the controllerFunc inside the returned middleware
function controllerWrapper(controllerFunc) {
return async function (req, res, next) {
try {
const data = extractData(req)
await controllerFunc(data)
} catch (err) {
next(err)
}
}
}
router.get('/', controllerWrapper(foo))

TypeError : next() is not a function

I am writing a middleware function that looks for validation errors and if the error is found gives out a certain output else continues the program flow. I have two functions with the exact code but they check for different schemas.
My first function runs without any exception. However, when I try to execute the second function I get an error in the console.
const validateCampground = (req, res, next) => {
const { error } = campgroundSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next();
}
};
const validateReview = (req, res, next) => {
const { error } = reviewSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next(); //this is the point where the exception occurs
}
};
It is only inside the validateReview function where next middleware function is not recognised as a valid function.
The problem was not with the next() middleware but instead it was with the route as I was wrapping the route with the validateReview function.
I was doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview(
catchAsync(async (req, res) => {
//my Logic here
})
));
Whereas , I should have been doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview,
catchAsync(async (req, res) => {
//my logic here
})
);
hi if you want to use a middileware
exports.middileware = (req,res,next)=>{
try{
//middileware logic
next();
}catch(err){
//print the error
})
}
}
and call the exported middileware file in requires file to check the middileware function
const { middileware } = require('path');
and use like this
router.get('/routename',middleware,nextfunction) //router you can choose as you like get,post,patch anything
try this out
I got this error when I omitted "req" and "res" in the function's parameters. When I added them, the error disappeared. Since I was using typescript, the first scenario looked like this:
function traceRoute(next){
console.log(routeTrace);
next();
}
Corrected to:
function traceRoute(req, res, next){
console.log(routeTrace);
next();
}

How to consume data from firebase db in a middleware to authenticate users

What I'm trying to do is to consume data from a firebase databse. I'm trying to achieve that from a middleware, but I couldn't do that. I'm wondering if this is the best way to do this. This is my code:
exports.userExist = function (req, res, next) {
(async () => {
try {
var query = db
.collection("users")
.where("user", "==", req.query.user)
.where("password", "==", req.query.password);
query.get().then(function (querySnapshot) {
if (querySnapshot.size > 0) {
res.json(true);
next();
} else {
res.json(false);
next();
}
});
} catch (error) {
return res.status(500).send(error);
}
})();
};
My doubt is how can I consume this method from my middleware, I'm trying to do something like that:
function verifyUser(req, res, next) {
let user= userController.findUser; //Hi have doubt about of how consume the middelware..
if(user!=null){
//The rest of the code.
}
next();
}
Is it the correct approach? or maybe I'm trying to achieve this wrong?
Couple of problems with your code.
There is no need for an async IIFE inside a middleware. The middleware function itself can be an async function.
If you call res.json(), that ENDS the request, and sends the response. You likely don't want that behavior here.
exports.userExist = async function (req, res, next) {
try {
var query = db
.collection("users")
.where("user", "==", req.query.user)
.where("password", "==", req.query.password);
const querySnapshot = await query.get()
if (querySnapshot.size > 0) {
// assume the query only returns 1 user?
req.userObj = querySnapshot.docs[0].data()
}
next();
} catch (error) {
return res.status(500).send(error);
}
};
Then, in downstream handlers:
function verifyUser(req, res, next) {
if (req.userObj) {
// previous middleware found a user
} else {
// previous middleware did not find a user
}
next();
}
Example usage:
app.use(userExist)
app.use(verifyUser)

nodeJS (express,connect) - Dynamically add middleware in current flow

I am currently working on formBuilder (client javascript <=> JSON <=> node), so i need effective way to handle JSON data on server. All forms are bind on one route, catched by middleware, so i need something like this:
Code is simplified (no regexs, req validators etc ..)
var middleware = require('../middleware'); // simple dir to object export
exports = module.exports =function(req,res,next) {
if(req.xhr && req.is('application/json')) {
var i, items = req.body.events.length;
for(i = 0; i < items; i++) {
var event = req.body.events[i];
if(middleware.forms[event] {
// -----------------
and here add that middleware into current flow ..
// -----------------
}
}
} else {
return next();
}
Easiest way is to prepare list of middleware, which will be used and call them in final route witch async .. but that i donw regard this as good way ..
So, i there any way to add requested middlwares to current flow, but before filan route ?
Middleware are just functions. So there is nothing wrong with just calling them. I had the same problem last week and I wrote a little helper.
var walkSubstack = function (stack, req, res, next) {
if (typeof stack === 'function') {
stack = [stack];
}
var walkStack = function (i, err) {
if (err) {
return next(err);
}
if (i >= stack.length) {
return next();
}
stack[i](req, res, walkStack.bind(null, i + 1));
};
walkStack(0);
};
You can use it with an array or just one function.
walkSubstack(middleware, req, res, next);
//or
walkSubstack([middleware, middleware], req, res, next);
I wrote something very similar:
let isActive1 = false;
let isActive2 = false;
let func1MD = (req, res, next) { /* ... */ }
let func2MD = (req, res, next) { /* ... */ }
let middleware = (function () {
// middleware #1
function func1(req, res, next) {
if (!isActive1) { return next(); }
return func1MD.call(null, req, res, next);
}
// middleware #2
function func2(req, res, next) {
if (!isActive2) { return next(); }
return func2MD.call(null, req, res, next);
}
// Returning an array of all middlewares to be called sequentially
return [
func1,
func2
]
})();
app.use(middleware);

Resources