Global and general Error Handling of Mongoose with Express? - express

I'm building an Express API Server with Mongoose,
with Express i'm catching all errors and response with the error message
app.use(function (err, req, res, next) {
res.status(404).json({message: err.message})
})
But with Mongoose, i should pass each Error manually to next() each time when i do a query.
Is there any way that make mongoose throw the error to next() automatically so that i could never concern about the error handling of a query anymore?

Related

Express.js API robust error handling solution

I'm trying to improve my code below with error handling such that I can use the error handling code else where in my application where it's relevant.
I have the following sequence of events:
Route handler --> middleware --> service --> DAO --> MongoDB
I want to implement a robust error handling solution that I can use throughout the application.
I ran into an issues where when DB is down or CRUD operation fails, DAO does not handle the error and propagate it to relevant levels. I certainly need to be able handle HTTP 500 type errors so that my API is robust. Any suggestions highly appreciated.
As someone who ran into such "robust error-handling solutions" a few times (usually implemented as a middleware) I highly recommend against it:
Different routes will experience different edge cases which you'll want to handle separately. Trying to create a "silver bullet" that will handle everything will be inherently more complex and more difficult to maintain.
Further, due to the async nature of many routes, you may find yourself reading stacktraces of that error-handler without having the context of which route triggered it...
Yes, you can improve your code and use a centralised error handler in case something goes wrong and the error is not already handled in your controller.
Let us create a simple app to understand the code flow and centralised error handling. Please note that I am writing the below code in
StackOverflow editor hence running it directly might give syntax errors.
|- src
|-- app.js
|-- controller
|-- middleware
|-- routes
app.js
const express = require("express");
const routes = require("./routes");
const app = express();
// use /api endpoint for backend routes
app.use("/api", routes);
app.listen(8080);
middleware/error.js
function errorHandler(err, req, res, next) {
console.log("some error has occurred");
res.status(503).json({
msg: "something went wrong"
});
}
module.exports = errorHandler;
route/index.js
const express = require("express");
const router = express.Router();
const errorHandler = require("./middleware/error");
const HelloController = require("./controller/Hello.js");
router.route("/hello", HelloController);
router.use(errorHandler);
module.exports = router;
controller/Hello.js
function HelloController(req, res, next) {
try {
// Do some stuff, call dao
// Any Special Error Handling will go here
res.json({ msg: "success" });
} catch (e) {
// Any other error will call the errorHandler and send the 503 to the client thus preventing the
// server to crash
next(e); // this will call the errorHandler
}
}
module.exports = HelloController;
The request flow will be as follows
App.Js -> route/index.js -> controller/Hello.js -> IF error in controller a) middleware/error.js, else b) exit
You can add more categorisation in your code based on routes like /api/public, /api/private, /api/admin

How to log Celebrate validation errors to the console in Express.js?

I have an Express.js app in which I'm trying to log the validation errors returned by Celebrate to the console so that I can analyze them with the logging service that I use (which is GCP's Cloud Logging).
I'm currently just using the error handling middleware provided by Celebrate as suggested in the documentation:
// app.js
const { errors } = require('celebrate');
...
app.use(errors());
...
How can I extend the middleware (without re-implementing it) so that it also logs the validation errors to the console?
The simplest way to achieve this seems to be by defining another error middleware before the Celebrate error middleware, that checks whether the error is a Celebrate error (using the isCelebrateError method) and if so it logs it to the console:
// app.js
const { errors, isCelebrateError } = require('celebrate');
...
// middleware to log Celebrate validation errors
app.use((err, req, res, next) => {
if (isCelebrateError(err)) {
console.error(err);
}
next(err);
});
// Celebrate middleware to return validation errors
app.use(errors());
...
It is important to include the logging middleware before Celebrate's errors() middleware since errors() returns a JSON response and no other middleware is run after it (you can check out the Celebrate source code for the implementation details of errors()).

When to use () in a middleware and when to use just reference

var cookieParser = require('cookie-parser')
var cookieValidator = require('./cookieValidator')
var app = express()
async function validateCookies (req, res, next) {
await cookieValidator(req.cookies)
next()
}
app.use(cookieParser())
app.use(validateCookies)
// error handler
app.use(function (err, req, res, next) {
res.status(400).send(err.message)
})
app.listen(3000)
In the above code both cookieParser() and validateCookies are middlewares but the way of executing them are different. There is a function like () with cookieParser but not with validateCookies. Can someone please explain why? Sorry If I am sounding foolish.
The app.use() function takes a single parameter, which is a function reference for a function that serves as a middleware for your router in Express.
You’ve correctly included the reference to your validateCookies function without parenthesis, as you just want Express to know which function you’d like it to use as middleware, and not to execute it at the time (Express will invoke the function for you when it’s time).
cookie-parser is a bit of an outlier (and I can see from where your confusion stems). The cookieParser() function actually returns a function reference upon successful execution. This design is likely because this particular module allows developers to pass in certain values to change the resulting function’s behavior as a middleware.

Do I *need* to use next(err) with Express.js?

Basically, the question is do I need to use next(err) when encountering any errors? The nodejs error documentation says it is fine to use a standard sort of if(err) else... for asynchronous callbacks and EventEmitters, as long as the error isnt handled with a try-catch block for non async-await functions, as it will cause crashing. If I do need to use them, what is to prevent the next() function being called multiple times in the same handler for different asynchronous operations? Wouldnt using the default error handler cause headers to be sent multiple times and cause an error of its own when using event emitters ?
Apologies if the question has been asked, its just I cannot find a specific answer to why usage of express.js error handling is preferred.
If you are asking if you need to use an explicit next(err) in a handler,
e.g.
app.get('/someurl', (req, res, next) => {
//do something - whoops had an error
next(err);
})
No, the above is not required in a handler.
The only time you would need to explicitly wrap or pass on the error is if you have, for example, used a try/catch and are not handling the error condition itself in the handler, i.e. not returning a response in the handler (Not sure why you would want to do that).
What will happen above when an error occurs in the handler, express will continue on through the middlewares until it finds a handler that will deal with your error.
If there are none, it will exit.
So to use a global error handler, you could write your app like the following and not worry about next(err) in each handler function.
app.get('/route/one', async (req, res) => {
// do something that could throw an error
const result = await aFunctionThatCouldThrowAnError();
// No error handling in this function
res.json({ result });
});
app.get('/route/two', (req, res) => {
res.json({ hello: 'world-two' });
});
// A Global Error handler
app.use((err, req, res, next) => {
//handle all errors here
if(err) {
res.status(500).send('some error message')
}
res.status(404).send('not found');
});
Note that the order of middlewares is important, so the global error handler should be applied last.

Express handling CSRF error

How can I implement a custom error handler in Express using CSRF middleware after users click the back button in browser and resubmit the form? By default Express return a 403 page with lots of stack traces. I want to replace it by for example redirecting user to a custom error page. How can I do that?
Here are some examples of writing custom error handlers in Express: https://github.com/visionmedia/express/blob/master/examples/error-pages/index.js
Here are the custom error handlers I use: Error handling in an Express route
You might also want to consider modifying connect to return a different code than 403 when CSRF fails. You can change it here: https://github.com/senchalabs/connect/blob/master/lib/middleware/csrf.js#L82
You might choose 428 Precondition Required. The full list is here: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
That way, you could have a special message shown only for CSRF failures.
Like any other well designed middleware csurf passes the error to next. So it's possible to react on the raised error in the following way:
var csurf = require('csurf')();
app.use(function (req, res, next) {
csurf(req, res, function (err) {
if (err) {
// do what ever with err
} else {
next();
}
});
});