I am an absolute NodeJS beginner and want to create a simple REST-Webservice with Express and Mongoose.
Whats the best practice to handle errors of Mongoose in one central place?
When anywhere an database error occurs I want to return a Http-500-Error-Page with an error message:
if(error) {
res.writeHead(500, {'Content-Type': 'application/json'});
res.write('{error: "' + error + '"}');
res.end();
}
In the old tutorial http://blog-next-stage.learnboost.com/mongoose/ I read about an global error listener:
Mongoose.addListener('error',function(errObj,scope_of_error));
But this doesn't seem to work and I cannot find something in the official Mongoose documentation about this listener. Have I check for errors after every Mongo request?
Simpler and more up to date solution IMO:
Handle connection level errors when you instantiate your db:
const mongooseConnection = mongoose.createConnection(databaseURL)
mongooseConnection.on('error', err => {
throw new Error('Mongo database connexion error')
})
Query level errors may appear if you write fields with wrong types or use query options (populate, sort...) incorrectly. We can use a more readable/up to date try catch await
over the old promise.callback.catch
syntax (mongoose doc)
try {
await Band.findOne({ _id: badId }).exec();
} catch (err) {
throw new Error('Mongo database connexion error')
}
=> Use an error handler across all your app
Error handling in the 2 example above are not very relevant because you loose all contextual informations (mongo error msg, stack trace, database name, userId...)
You may handle errors in a centralized way so that you can pass contextual informations alongside the error message like so:
try { ... } catch (err) {
throw ApplicationError(
'myMessage',
{
// Here we pass additional informations
// this will help handling the error on the express api side
httpCode: err instanceof mongoose.Error.ValidationError ? 422 : 500,
// with this one you can display original error messsage/stack trace
originalError: err,
// other contextual informations may be useful to display
// but be carreful not to display sensitive informations
// here (Eg DB connection string)
databaseName,
userId,
userRole,
methodName,
}
)
}
class ApplicationError extends Error {
constructor(errMsg, additionalInfos) {
// Here handle error as you want
}
}
List of all error types thrown by mongoose
=> Use a request handler to handle all mongoose queries the same way
A common pattern to avoid duplicating code is to create a function to handle the execution of mongoose promise for you:
async function afterRequest(mongoosePromise, { sort, page, limit }) {
try {
if (sort) promise.sort(sort)
// PAGINATION
if (page) promise.skip(localConfig.page * limit || 25).limit(limit || 25)
else if (limit) promise.limit(limit)
return await promise.exec()
} catch (err) { /** handle error like in the above code */ }
}
So we can now use it like:
const mongoosePromise = Band.find()
afterRequest(mongoosePromise, { page: 1, limit: 10 })
This pattern has the advantage of:
The above is juste a very simplified example, but to go further, here are some features we could implement:
{ companyId: user.companyId }