In Node.js, should I use errors for flow control, or should I use them more like exceptions?
I'm writing an authentication controller and some unit tests in Sails.js, and currently, my registration method checks to see if a user with the same username exists. If a user already exists with the username, my model method calls its callback arguments with a new Error object, like so:
Model:
exists: function (options, cb) {
User.findOne({
where: { username: typeof options === 'Object' && options.username ? options.username : options },
}).exec(function (err, user) {
if (err) return cb(err);
if (user) return cb(new Error("A user with that username already exists."));
cb(null, !!user);
});
},
Controller:
User.exists(req.body.user.username, function (err, exists) {
if (err) {
console.log("error: ", err);
return res.status(409).json({
message: err
});
}
User.create(req.user).then(function (data) {
res.status(201).json({
user: data
});
});
});
Is this best practice? I'm not sure if node conventions favor errors for exceptional cases, or for flow control. I'm thinking I should rewrite this, but I want to know conventions before I do so. I think I've seen some examples written this way in Sails. Thanks!
Node (or Javascript really) can throw errors on exceptions by using the keyword
throw
:You can also add additional parameters to the default Error object to give more semantic meaning to your errors in your program. With that said, you wouldn't want to throw an error in your route handler because this would crash the process and your server.
When using route handlers in Sails (or express really), you most certainly should check the type of error and respond to the client accordingly.
Notice that in the case that the DB responds with an internal error, we pass to the
next()
function which is picked up by error handling middleware down the chain. Any middleware with an arrity of 4 is defined as error handling middleware. Sails probably has some default error handler, but you can probably also override it — you'll need to check the appropriate docs for this information as I prefer the control I gain by using Express alone.The above answer is good for Express, but in Sails controllers you should not be calling
next
; best practice is to always return a response. In most example Sails code you won't even seenext
as an argument to a controller action function. Also note that Sails comes with some default response methods built right into theres
object, such asres.serverError
andres.badRequest
, as well asres.negotiate
which will attempt to route the error to the appropriate handler for you based on the status code. So your example could be tweaked as:Model:
Controller: