Mongoose: Consume error in post hook - mongodb

I have an Express.js app backed with MongoDB using Mongoose. I need to ignore a duplicate key error from MongoDB (error code 11000) and still return a 204 HTTP response. The idea is to use the post hook on save, consume the error and ignore it.
Service layer
const createMyModel = (req, res, next) => {
MyModel.create({...data})
.then(createRes => res.status(204).send())
.catch(next)
}
Schema - save hook
MySchema.post('save', (err, res, next) => {
if (!err || (err.name === 'MongoError' && err.code === 11000)) {
// The duplicate key error is caught here but somehow
// the catch on my service layer gets triggered
next();
}else{
next(err)
}
});

The next callback in Mongoose keeps track of something called a firstError. This is where internal errors like Duplicate Key Error get stored. This prevents the user from overriding the error state and calling next would always result in checking for firstError and triggering a promise rejection even if one tries to call next() or next(null).

If you want to ignore them completely I think you could set emitIndexErrors to false in the schema.options object.
http://mongoosejs.com/docs/guide.html#emitIndexErrors

Related

Using .catch with async/await

I am wondering if I can still use .catch() within an async function to catch the error instead of using a try-catch block.
The following code is from my project using MongoDB and Express:
router.get('/communities', EnsureAuthenticated, async (req, res) =>{
//Look up the user in the db + populate the community field
const userInfo = await User_DB
.findOne({ _id:req.user._id, })
.populate('communities')
.lean()
.catch(err => {
console.log(err)
res.status(500)
.json({
msg: 'DB: Error Fetching User Info',
});
// rest of the functions that take userInfo as the input
});
When using try-catch all variables will be limited to within the scope of that try-catch block.
If I need to use the userInfo as the input for other functions down the line I'll have to put everything within that try-catch block which doesn't look clean and can be confusing. Because you don't know which function does the error belongs to if there is any.
Is my understanding correct?
I apologize for the formatting. I'm doing this on my phone.
You can also make a central error handler, you can find an example here

How to handle non explicit errors inside sails.js helpers?

I am trying to figure out how the Error handling in Sails.js works. Unfortunatley the code examples in the docs do not cover this use case.
The problem is I keep getting this error:
UsageError: `.intercept()` handler returned `undefined`, but this should never happen.
Regardless, here is a summary of the original underlying error:
Now all I am trying to do is call a helper and if it fails, then I want to catch the error (any), log it and run some code. If I wouldn't be using Sails but normal promises I would have handled it like this:
await helper().catch((err) => { // run some code }
In Sails I should be able to use .intercept() instead of .catch()
My code looks like this:
// ExportController.js
const csv = await sails.helpers.files.convertToCsv(data)
.intercept((err) => {
sails.log.error(err)
req.addFlash('error_messages', 'Error parsing data to csv!')
return res.redirect(`/`);
})
// convert-to-csv.js
if (!Array.isArray(inputs.data)) {
throw new Error('invalid inputs.data type: ' + typeof inputs.data)
};
Now how can I avoid getting this error?
The code examples show only cases where errors that are explicitly added to the exits object are handled, but not for general error handling.
In the docs it says that if the filter argument is
not provided, ALL errors will be intercepted.
Or is that only true for db queries? Because the .intercept() doc section is in that subcategory.
You could use “throw ‘errorCode’;” for example:
Set the exits:
exits {
errorWithCsvFile: {
responseType: 'badRequest'
}
}
const csv = await sails.helpers.files.convertToCsv(data)
.intercept(‘somethingWrongCode’, ‘errorWithCsvFile’)
... // Other handles
.intercept(err => new Error(err))
Alternative:
try {
...
const csv = await sails.helpers.files.convertToCsv(data)
.intercept((err) => {
sails.log.error(err)
req.addFlash('error_messages', 'Error parsing data to csv!')
throw 'badRequest';
})
...
} catch (err) {
sails.log.err(err);
return res.redirect(`/`);
}

Suppressing/Catching the ValidationError error from Mongoose

I have a static method setup that will verify if an object follows the schema of the model properly, and I've got the validation itself working just fine, but I can't get Mongoose to hide the ValidationError error that displays in the console.
The static method for the validation is:
fieldSchema.statics.testValidate = function( field ) {
return new Promise( ( res, rej ) => {
new this( field ).validate( err => {
if ( err ) return rej( err )
res( true )
} )
})
}
Then when I execute it:
Field.testValidate( fieldData )
.then(data => {
console.log('OK!', data)
})
.catch( err => {
console.error('FAILED:',err)
})
.finally(() => Mongoose.connection.close())
And like I said, I can get it to validate the data, but it will always show this error:
Unhandled rejection ValidationError: Field validation failed
at MongooseError.ValidationError ....
(Note: Im using Bluebird for my JS promises, incase it has anything to do with the then/catch somehow)
The script execution keeps going on with the code, so that means that it doesnt actually kill the script or anything, it just logs the error to the console. But since this function is meant to validate, its not really an "error", so is there a way to hide this from the console? (Just for this function, not for ALL Mongoose errors)
Thanks
validate already returns a promise so you don't need to wrap it in a new promise. The reason you see the error logged to the console is because of the fact that you're wrapping it in a new promise thus preventing the original promise from ever being caught i.e. Unhandled rejection.
fieldSchema.statics.testValidate = function(fieldData) {
return new this(fieldData).validate()
}

Error Handling in Sails.js

I am just starting using Sails.js and it's an amazing framework. But I've met some situation and I cannot find solution by Google so I came here for help.
I have a controller to connect to another remote service with very old-designed API full of XML response and inconsistency, wrapping that service in simple and clean APIs. So I have some routers like:
list: function(req, res) {
params = {
...
}
FooService.request(data, function(error, response) {
res.send(response)
})
process.once('uncaughtException', function(err) {
res.send(500, '[Foo] ' + err);
});
},
The 'process.once' is for async exceptions which may raised in the FooService.request process. I know this is bad code and my question is: how to handle such situation more Sails.js way?
In Node.js we have Domain and connect-domain, which are designed for such problems. Because Sails.js is basically Express, which can facilitate connect-domain very well, I think there may be some idiomatic way to do that.
I've tried adding this in config/local.js:
module.exports = {
...
express: {
customMiddleware: function(app) {
console.log('Custom middleware config called')
var domain = require('connect-domain')
app.use(domain())
.use(function(err, req, res, next) {
console.log('Error catched!')
res.send(500, '[Foo] ' + err)
})
}
}
};
When un-catched exception occurred, it will not crash server and error 500 being returned to client side ('app.use(domain())' works). But the custom error handler does not called. Have no idea why.
If you're in control of the FooService code, then the best option is to handle all errors that happen there by calling the callback for FooService.request early with the error, and then using res.serverError or some other response in your controller:
FooService.request(data, function(error, response) {
if (error) {return res.serverError(errror);}
res.send(response)
});
If the FooService is using packages that you don't control, which may themselves throw errors inside of async code that they aren't catching (bad code!) then another good option is to use Node's error domains. See this answer for an example of someone doing a quick wrapper to use domains to catch errors in asynchronous code.

Error handling with Mongoose

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?
If you're using Express, errors are typically handled either directly in your route or within an api built on top of mongoose, forwarding the error along to next.
app.get('/tickets', function (req, res, next) {
PlaneTickets.find({}, function (err, tickets) {
if (err) return next(err);
// or if no tickets are found maybe
if (0 === tickets.length) return next(new NotFoundError));
...
})
})
The NotFoundError could be sniffed in your error handler middleware to provide customized messaging.
Some abstraction is possible but you'll still require access to the next method in order to pass the error down the route chain.
PlaneTickets.search(term, next, function (tickets) {
// i don't like this b/c it hides whats going on and changes the (err, result) callback convention of node
})
As for centrally handling mongoose errors, theres not really one place to handle em all. Errors can be handled at several different levels:
connection errors are emitted on the connection your models are using, so
mongoose.connect(..);
mongoose.connection.on('error', handler);
// or if using separate connections
var conn = mongoose.createConnection(..);
conn.on('error', handler);
For typical queries/updates/removes the error is passed to your callback.
PlaneTickets.find({..}, function (err, tickets) {
if (err) ...
If you don't pass a callback the error is emitted on the Model if you are listening for it:
PlaneTickets.on('error', handler); // note the loss of access to the `next` method from the request!
ticket.save(); // no callback passed
If you do not pass a callback and are not listening to errors at the model level they will be emitted on the models connection.
The key take-away here is that you want access to next somehow to pass the error along.
hey this is the simplest way i found..
try { } catch (error) {
console.log(error);
// checking validation
if (error.name === "ValidationError") {
const message = Object.values(error.errors).map(value => value.message);
return res.status(400).json({
error: message
})
}
res.status(400).json(error.message)
}
}
just copy paste