How to pass express arguments to nodejs asynchronous calls - node.js

Here is the problem that I am facing in express.
Somewhere in my express middleware, I want to check for the presence of a file.
//Setting up express middeleware...
app.use(f1);
app.use(f2);
...
function f1(req, res, next) {
...
//Here I want to check if 'somefile' exists...
fs.access('somefile', callback1, req, res, next);
}
//In the callback, I want to continue with the middleware...
function callback1(err, req, res, next) {
if (err) {
//Report error but continue to next middleware function - f2
return next();
}
//If no error, also continue to the next middleware function - f2
return next();
}
function f2(req, res, next) {
}
How do I pass req, res, next as arguments to the callback of fs.access?
The above code does not work. I suspect I need to use closures but how?
A totally different way of looking at the problem is: How do I use, for example, fs.access, itself as a express middleware function?

For me this approach have much more sense:
Assume you want to create a middleware in f1, and then have a middleware for error handling handleError, and any other middleware.
For f1 you already have the req, res in the closure so you will have access in fs.access callback.
function f1(req, res, next) {
fs.access('somefile', (err) => {
if (err) return next(err);
// here you already have access to req, res
return next();
}
}
function f2(req, res, next) {
// do some stuff if there is err next(err);
}
function handleError(err, req, res, next) {
if (err) {
// handle somehow the error or pass down to next(err);
}
}
app.use(f1); // you pass down the err |
app.use(f2); // ------------ |
app.use(handleError); // <----|

Related

How to catch all errors in route as a middleware in ExpressJS?

I have a problem and I haven't found a solution yet. I want to catch all errors if occur any error in each route but it's very inconvenient when I have to do it many times.
How can i implement it as a middleware same like app.use(ErrorHandle); ?
Code in ErrorHandler.js:
export const ErrorHandler = func => async (req, res, next) => {
try {
await func(req, res, next);
} catch (error) {
next(error);
}
}
Code in index.js
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
Code in route need to catch error:
import { ErrorHandler } from './ErrorHandler';
export const uploadMedia = ErrorHandler(async (req, res) => {
// do something...
let error = new Error();
error.statusCode = 404;
error.message = 'Content not found!';
}
Sorry if misunderstood your question... When you do the code below which you provided, you are assuming that if an error reaches the end of the stack via next(err), such handler should be called. Hence it's the last declaration after all your routes.
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
That, however, won't catch unhandledExceptionErrors. You still need the good old
try {
// throw error somewhere
} catch (e) {
next(e);
}
Personally, I haven't tried this package but it seems so be a nice hack for Express router's inability to handle promise returns. About express-promise-router:
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.

Using Express how do I redirect a client to an error page when an error is thrown inside an interior function?

Question: Using Express how do I redirect a client to an error page when an error is thrown inside an interior function? Logging the error from a function several layers deep is no problem, the redirection is what I don't understand.
Example that works: The following simplified code doesn't use an interior function and successfully redirects the client to an error page when an error is thrown.
app.get('/', function(req, res, next) {
let cat = 10;
try {
if (cat === 10) throw new Error('cat should not be 10');
} catch(error) {
return next(error);
}
res.render('homepage');
}
);
// custom error handler
app.use( function(error, req, res, next) {
let filePath = path.join(__dirname, '/error-pages/400.html');
res.status(400).sendFile(filePath);
});
Uh oh, this one doesn't work: Unfortunately, when an interior function is used the client always gets to res.render('homepage'). In real code the error is almost always going to be several layers deep so there must be a logical solution to this common problem.
app.get('/', function(req, res, next) {
interiorFunction(next);
res.render('homepage');
}
);
function interiorFunction(next) {
let cat = 10;
try {
if (cat === 10) throw new Error('cat should not be 10');
} catch(error) {
return next(error);
}
}
// custom error handler
app.use( function(error, req, res, next) {
let filePath = path.join(__dirname, '/error-pages/400.html');
res.status(400).sendFile(filePath);
});
I interpret interior function as an intermediate middleware.
The way you showed it always hit the res.render('homepage'), also you are not calling next() when the middleware has no error, you can fix it by doing:
app.get('/', interiorFunction, function (req, res, next) {
res.render('homepage');
});
function interiorFunction(req, res, next) {
let cat = 11;
try {
if (cat === 11) throw new Error('cat should not be 10');
next();
} catch (error) {
return next(error);
}
}
EDIT
That was a solution based on middleware, but that seems that its not your case so the alternative is to have some logic to not call res.render('homepage') when there is an error. This is common to both the approaches: they both don't run the res.render('homepage') statement, in the end all resumes to if that code is run or not. So as an very simple working example:
app.get('/', function (req, res, next) {
const ok = interiorFunction(next);
if (ok) {
res.render('homepage');
}
});
function interiorFunction(next) {
let cat = 11;
try {
if (cat === 11) throw new Error('cat should not be 10');
return true;
} catch (error) {
next(error);
return false;
}
}
// custom error handler
app.use(function (error, req, res, next) {
let filePath = path.join(__dirname, '/error-pages/400.html');
res.status(400).sendFile(filePath);
});
EDIT 2:
Or even better:
app.get('/', function (req, res, next) {
try {
interiorFunction();
res.render('homepage');
} catch (error) {
console.log(error);
next(error);
}
});
function interiorFunction() {
let cat = 11;
if (cat === 11) throw new Error('cat should not be 10');
}
So the pattern here is not to pass the next function to interior functions, it don't make sense at all since they don't need to know that next exist, but instead make them throw exceptions and in the top caller, use a catch block to call next(error) and at the same time you're not running the res.render('homepage') line

Express error middleware without `next()`?

I'm using Express and I want to use the build-in middlewares to handle errors.
I'm using it this way -
app.use((err,req,res,next)=> {
console.log('error');
)};
Now this middleware is reached only if I'm using next(error) (where error is object of type Error) in my router.
On the other hand, if I simply throw new Error() in my router, the middleware is not called.
Is there any way to omit the next()?
So that if any error is thrown in my router, it will be handled by the middleware?
If you can use Node.js >= 8 that comes with async/await then you can use the following wrapper function over your routes:
const wrap = fn => {
return async function(req, res, next) {
let e = null;
try {
await fn(req, res, next);
} catch (err) {
e = err;
next(err);
}
if (!e) {
next();
}
};
};
app.get(
"/path",
wrap(async (req, res) => {
// use sync style code here which can throw
})
);
If the throw is synchronous, you could invent your own request handler wrapper that would catch any exceptions and turn them into a call to next():
app.get2 = function(path, fn) {
app.get(path, function(req, res, next) {
try {
fn(req, res, next);
} catch(err) {
next(err);
}
});
};
And, sample usage:
app.get2("/test", function(req, res, next) {
throw new Error("testing..."); // will end up in next()
});
Then, if your fn function throws synchronously, then it will be caught by your get2() wrapper and will automatically call next(err) for you.
If you're throwing asynchronously, this won't work. There is no generic wrapper for asynchronous throws because throws into an asynchronous callback don't throw to your code - they throw back into the asynchronous infrastructure for whatever async operation you were using and only they can catch it. If you use promises exclusively for your asynchronous operations, then the promise infrastructure will automatically catch your throws and turn them into rejected promises and you could do something similar to above for a returned and rejected promise. The Koa framework (a derivative of Express) does something similar already and I think Express 5 will have some features like that to.

ExpressJS middleware that also catches errors?

I'm writing middleware that I'm applying at the route level, like so:
router.get('/foo', myMiddleware, (req, res) => { ... });
so I can do some stuff with the request. But I also need to catch errors to do some special handling. I know I can add a handler afterwards like this:
... (req, res) => { ... }, myErrorHandler);
and it'll get called just fine.
But my question is, is there any way to have a single piece of middleware that can do all of this so I don't need two points of integration? I tried calling req.on('error', (err) => { ... }) within my middleware but it never seems to be called.
Express comes with a built-in error handler that takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
// Router
router.get('/foo', myMiddleware, (req, res) => { ... });
// Router Error Handler
router.use(function (err, req, res, next) {
});
I ended up solving this by writing a helper function that wraps the actual handler. It looks like this:
function checkPage(handler: express.RequestHandler) {
return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _write = res.write;
res.write = chunk => {
if (req.query.verbose) {
return _write.call(res, `<p>${chunk}</p>`);
} else {
return true;
}
}
try {
await handler(req, res, next);
res.write('<hr/><p style="color:green;"><b>happy</b></p>');
} catch (err) {
res.write(`<p style="color:red;">${err}</p>`);
res.write('<hr/><p style="color:red;"><b>SAD!</b></p>')
}
res.end();
}
}
Then in my route handler, I just use it like so:
router.get('/foo', checkPage(async (req, res, next) => {
...
res.write('stuff');
...
}));

Calling next inside a helper-function?

I'm trying to setup a simple system for rendering user-input-errors and stopping propogation in express, this is what I've got so far:
routingFunction= (req, res, next) {
//setting up a test in express-validator
var test = req.assert('access_token', 'required').notEmpty();
isValid(test, next);
//non error stuff
}
isValid = (tests, next) => {
//some more code here, that checks if any errors were found and save tem to the array errors.
if(errors.length > 0){
return next(new Error());
}
};
//a middleware that catches errors:
app.use((err, req, res, next) => {
res.json('error').end();
});
My problem with this is that it doesn't stop the propagation when I'm calling Next(new Error());, I could return true/false from isValid and then return next(new Error()), but that would add a lot of bloat to my controllers, is there some better way to do it from within the helper function?
In main route file, e.g. routes/index.js
// Set of func(req, res, next)
let v = require('validator'); // middleware-validators
let user = require('user'); // routes for user
...
app.get('/smth-route-of-user', v.isTokenSet, v.isEmail, ..., user.get)
In middlewares/validator.js
let v = require('validator-func-list');
...
exports.isTokenSet = function (req, res, next) {
if (v.isValid(req.body.token))
next(); // Forwarding to next middleware. In our route next() called v.isEmail
else
next(new Error('Token is empty')); // Stop route chain and call error-middleware;
}
exports.isEmail = function (req, res, next) {
...
You can join validators to one, e.g. checkUser(), and use only one in route.
In middlewares/errorHandler.js
module.exports = function (err, req, res, next) {
let msg = err.message; // Here we see 'Token is empty';
if (req.xhr)
res.json(msg);
else
res.render('error_page.html', {message: msg, ...});
// Here we can call next(err) to forwarding error to next errorHandler. In example below it was errorHandler2.
}
In app.js don't forget to attach error-middleware to application.
app.use(require('middlewares/errorHandler'));
app.use(require('middlewares/errorHandler2'));
If you need collect errors then validator must push error to req.errors (or another field as you want) and call next() without error. In render middleware you simple check req.errors.length and show normal or error page.
Code after isValid(test, next); always execute. Code below block it, but imho is dirty.
routingFunction = (req, res, next) {
var test = req.assert('access_token', 'required').notEmpty();
if (!isValid(test, next))
return; // callback called by isValid. It's dust.
//non error stuff
...
next(); // callback must be called
}
isValid = (tests, next) => {
if(errors.length > 0){
next(new Error());
return false;
}
return true;
};
More better use like this
routingFunction = (req, res, next) {
var test = req.assert('access_token', 'required').notEmpty();
if (!isValid(test))
return next (new Error('error description'));
//non error stuff
...
next();
}

Resources