Turns errors from database libraries into more useful error objects
npm install database-errorA small library to make error objects from databases more programmatically useful. Currently supports:
__PostgreSQL:__ Through pg (not* pg.native!).
Database libraries tend to throw rather hard-to-process errors - often, they will contain a mishmash of information in a single error message string, with no clear way to filter or handle them programmatically. This library solves that by parsing the errors for you, and turning them into more consistent, useful, identifiable, and structured error objects.
Note that while Knex is supported (since it uses pg internally, for talking to PostgreSQL), it is not required - this library should work fine with any other pg-based implementation as well. Support for other (relational) database libraries is planned in the future, with the intention to provide a standardized format for useful database errors.
WTFPL or CC0, whichever you prefer. A donation and/or attribution are appreciated, but not required.
Maintaining open-source projects takes a lot of time, and the more donations I receive, the more time I can dedicate to open-source. If this module is useful to you, consider making a donation!
You can donate using Bitcoin, PayPal, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else. Thank you!
Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the files in src/, not those in lib/.
Build tool of choice is gulp; simply run gulp while developing, and it will watch for changes.
Be aware that by making a pull request, you agree to release your modifications under the licenses stated above.
A simple example, detecting a duplicate entry in the database:
``javascript
const Promise = require("bluebird");
const databaseError = require("database-error");
/ The below objects are used as 'object predicates' in Bluebird's .catch, to filter out the specific kind of database errors we're interested in. Errors are matched by the properties of the predicate object(s). /
let duplicateEmailAddress = {
name: "UniqueConstraintViolationError",
table: "users",
column: "email"
}
let duplicateUsername = {
name: "UniqueConstraintViolationError",
table: "users",
column: "username"
}
// ... assuming we have a Knex instance, and an Express application with an express-promise-router ...
router.post("/register", (req, res) => {
Promise.try(() => {
return knex("users").insert({
username: req.body.username,
email: req.body.emailAddress
}).returning("id");
}).then((id) => {
res.send(Hello, user ${id[0]}!);`
}).catch(databaseError.rethrow).catch(duplicateEmailAddress, (err) => {
res.status(422).send("That e-mail address already exists! Please pick a different one.");
}).catch(duplicateUsername, (err) => {
res.status(422).send("That username already exists! Please pick a different one.");
})
});
This assumes the following:
* A users table exists, with an id, username and email column.email
* The and username columns both have a UNIQUE constraint set on them, disallowing duplicates.
When a user attempts to register a new account, and either their provided username or e-mail address already exist in the database the following happens:
1. A generic error will be thrown by the database library (eg. pg).databaseError.rethrow
2. This error is picked up by in the first .catch statement, and turned into a more useful error (with the clearly distinguishable UniqueConstraintViolationError name, and structured table/column properties).databaseError.rethrow
3. This new, more useful error is rethrown by ..catch
4. The second and third statement each receive the new error, check whether it has the properties they're looking for, and if so, trigger the corresponding error handler.
A brief listing of the currently defined error types, for easier searching:
* UniqueConstraintViolationError
* ForeignKeyConstraintViolationError
* NotNullConstraintViolationError
* CheckConstraintViolationError
* InvalidTypeError
* EnumError
* UndefinedColumnError
The full documentation for each type is below.
Rethrows a more useful error, where possible.
* __error:__ The original database error object to convert.
Throws:
* __If the original error could be parsed:__ Throws the new, converted database-error error object. This will be one of the types specified below.
* __If the original error was not of a recognized type:__ Rethrows the original error, unmodified.
In practice, this means that unrecognized errors are "passed through", while recognized errors are converted into more useful errors.
__You would not normally use this method directly, and you should probably use databaseError.rethrow instead.__
Converts the specified error into a more useful Error object.
* __error:__ The original database error object to convert.
Returns/throws:
* __If the original error could be parsed:__ Returns the new, converted database-error error object. This will be one of the types specified below.databaseError.UnknownError
* __If the original error was not of a recognized type:__ Throws a .
Special error type that's thrown for databaseError.convert when an unrecognized error is provided. You will never encounter this error when using databaseError.rethrow, and it does not include any further information.
#### databaseError.DatabaseError
"Base type" that all other error types (except for UnknownError) are inheriting from. This can be useful for logging all recognized database errors.
However, note that you will never encounter a DatabaseError directly - it purely functions as a base type, meaning that you need to use instanceof to recognize an error as a DatabaseError, and matching by name is not sufficient.
Every error inheriting from DatabaseError will always contain the following properties:
* __originalError:__ The original error being converted.
__pgCode:__ Only for PostgreSQL.* The SQLSTATE error code from PostgreSQL.
* __code:__ A unique, string-typed identifier for the error type.
In addition, different errors include different other properties. These are listed for each error type below.
#### databaseError.UniqueConstraintViolationError
This error is thrown when a query violates a UNIQUE constraint. In practice, this means that there's an attempt to insert a duplicate entry into your database (see also the Usage section above).
A UniqueConstraintViolationError will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* For single-column UNIQUE constraints:UNIQUE
* __column:__ The column in which the violation occurred.
* __value:__ The offending duplicate value.
* For multiple-column (composite) constraints:UNIQUE
* __columns:__ The columns in which the violation occurred, as an array.
* __values:__ The offending duplicate values, as an array.
* __isComposite:__ Whether the constraint involves multiple columns. Boolean.UNIQUE
* __constraint:__ The name of the constraint that is being violated.
Note that the keys for composite UNIQUE constraints are __plural__, not singular. This is intentional, to provide a more predictable API.
#### databaseError.ForeignKeyConstraintViolationError
This error is thrown when a query violates a foreign key constraint. In practice, this means that there's an attempt to point a foreign key at a non-existent entry (usually in another table).
A ForeignKeyConstraintViolationError will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* __column:__ The column in which the violation occurred.
* __value:__ The offending foreign key (value).
* __constraint:__ The name of the foreign key constraint that is being violated.
* __foreignTable:__ The table to which the violating foreign key refers.
#### databaseError.NotNullConstraintViolationError
This error is thrown when a query violates a NOT NULL constraint. In practice, this usually means that a required value is missing.
A NotNullConstraintViolationError will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* __column:__ The column in which the violation occurred - ie. the column for which a required value is missing.
* __values:__ An array of values, for the item being inserted/updated. Note that __all these values are string representations__, and they are intended purely for reference purposes.
* __query:__ The query that caused the violation. This query may contain placeholders or be incomplete, and so is intended purely for reference purposes.
#### databaseError.CheckConstraintViolationError
This error is thrown when a query violates a CHECK constraint. This can mean a lot of things (as CHECK constraints can be fairly arbitrary), but one of the most common cases is probably an invalid value for a Knex-defined "enum", since Knex uses CHECK constraints rather than native ENUMs.
An CheckConstraintViolationError will contain the following additional properties:
* __schema:__ The schema in which the violation occurred.
* __table:__ The table in which the violation occurred.
* __column:__ The column in which the violation occurred - ie. the column for which an invalid value was specified. __This value is not always present.__ It is only present if the constraint name matches the