When a call to any model method or helper fails, Sails throws a JavaScript Error instance whose properties can be useful in diagnosing what went wrong.
Waterline normalizes these Error instances, classifying them with consistent err.name
values and, when applicable, err.code
:
try {
await Something.create({…});
} catch (err) {
// err.name
// err.code
// …
}
Catch-all error handling, while better than nothing, often just isn't enough. (There's a big difference between "that is not a valid username" and "we aren't able to create new users at all right now".) In order to negotiate the different kinds of errors appropriately, you'll need to be able to examine them in a granular way.
Fortunately, Sails provides some syntactic sugar for doing this out of the box, without resorting to try… catch: .intercept() and .tolerate().
await Something.create({…})
.intercept((err)=>{
// Return a modified error here (or a special exit signal)
// and .create() will throw that instead
err.message = 'Uh oh: '+err.message;
return err;
});
Property | Type | Details |
---|---|---|
name | The broad classification of the error. e.g. 'UsageError' |
|
message | See .message. | |
stack | See .stack. | |
code | A narrower classification of the error that is sometimes included. e.g. 'E_UNIQUE' |
When using code that interacts with Waterline (usually through model methods) there are a few different kinds of error you may encounter.
When an error has name: 'UsageError'
, this indicates that a Waterline method was used incorrectly, or executed with invalid options (for example, attempting to create a new record that would violate one of your model's high-level validation rules.)
This sort of error can come from any model method.
err.name === 'UsageError'
Adapter errors usually indicate a problem in the underlying adapter, and not in the request itself. This can happen when a database goes offline, when there is a permission issue, because of some database-specific edge case, or (more rarely) a bug in the adapter. This kind of error will have name: 'AdapterError'
.
This sort of error can come from any model method.
err.name === 'AdapterError'
A uniqueness error occurs when a value that should be unique matches that of another record in the database. While this is considered an adapter error, it has its own code
to differentiate it from a normal adapter error: code: 'E_UNIQUE'
.
This sort of error can only come from the .create()
, .update()
, .addToCollection()
, and .replaceCollection()
model methods.
err.code === 'E_UNIQUE'
The exact strategy you use to do this in your Sails app depends on whether you're using await
, promises, or callbacks.
await
To handle the different errors that may occur when attempting to create a new user from within an action:
await User.create({ emailAddress: inputs.emailAddress })
// Uniqueness constraint violation
.intercept('E_UNIQUE', (err)=> {
return 'emailAlreadyInUse';
})
// Some other kind of usage / validation error
.intercept({name:'UsageError'}, (err)=> {
return 'invalid';
});
// If something completely unexpected happened, the error will be thrown as-is.
return exits.success();
If you're not able to use await
because you're using Node.js <= v7.9, then prepare yourself: error handling works a bit differently when using callbacks or promise chaining instead of await
.
Please use
await
if at all possible! It is much safer for your app, your code will be cleaner, and you will be happier.
For example, if you're using promise chaining, here's how you might handle the different errors that could occur when attempting to create a new user:
User.create({
emailAddress: req.param('emailAddress')
})
.then(function (){
res.ok();
})
// Uniqueness constraint violation
.catch({ code: 'E_UNIQUE' }, function (err) {
res.sendStatus(409);
})
// Some other kind of usage / validation error
.catch({ name: 'UsageError' }, function (err) {
res.badRequest();
})
// If something completely unexpected happened.
.catch(function (err) {
res.serverError(err);
});
Here's the same example, but written with traditional Node.js callbacks instead of promise chaining:
User.create({
emailAddress: req.param('emailAddress')
})
.exec(function (err){
if (err && err.code === 'E_UNIQUE') {
return res.sendStatus(409);
} else if (err && err.name === 'UsageError') {
return res.badRequest();
} else if (err) {
return res.serverError(err);
}
return res.ok();
});
But beware uncaught exceptions!