Handle errors in a simple, stable, consistent way
npm install modern-errors







Handle errors in a simple, stable, consistent way.
Simple patterns to:
- ⛑️ Create error classes
- 🏷️ Set error properties
- 🎀 Wrap or aggregate errors
- 🐞 Separate known and unknown errors
Stability:
- 🚨 Normalize invalid errors
- 🛡️ 100% test coverage
- 🤓 Strict TypeScript types
- modern-errors-cli: Handle
errors in CLI modules
- modern-errors-beautiful:
Prettify error messages and stacks
- modern-errors-process:
Handle process errors
- modern-errors-bugs: Print
where to report bugs
- modern-errors-serialize:
Serialize/parse errors
- modern-errors-clean: Clean
stack traces
- modern-errors-http: Create
HTTP error responses
- modern-errors-winston:
Log errors with Winston
- modern-errors-switch:
Execute class-specific logic
- 🔌 Create your own plugin
Create error classes.
``js
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError')
export const UnknownError = BaseError.subclass('UnknownError')
export const InputError = BaseError.subclass('InputError')
export const AuthError = BaseError.subclass('AuthError')
export const DatabaseError = BaseError.subclass('DatabaseError')
`
Set error properties.
`js`
throw new InputError('Invalid file path', { props: { filePath: '/...' } })
Wrap errors.
`js`
try {
// ...
} catch (cause) {
throw new InputError('Could not read the file.', { cause })
}
Normalize errors.
`jsBaseError
try {
throw 'Missing file path.'
} catch (error) {
// Normalized from a string to a instance`
throw BaseError.normalize(error)
}
Use plugins.
`js
import ModernError from 'modern-errors'
import modernErrorsSerialize from 'modern-errors-serialize'
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsSerialize],
})
// ...
// Serialize error as JSON, then back to identical error instance
const error = new InputError('Missing file path.')
const errorString = JSON.stringify(error)
const identicalError = BaseError.parse(JSON.parse(errorString))
`
`bash`
npm install modern-errors
If any plugin is used, it must also be installed.
`bash`
npm install modern-errors-{pluginName}
This package works in both Node.js >=18.18.0 and
browsers.
This is an ES module. It must be loaded using
an import or import() statement,
not require(). If TypeScript is used, it must be configured to
output ES modules,
not CommonJS.
`js
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError')
export const UnknownError = BaseError.subclass('UnknownError')
export const InputError = BaseError.subclass('InputError')
export const AuthError = BaseError.subclass('AuthError')
export const DatabaseError = BaseError.subclass('DatabaseError')
`
Exporting and documenting all error classes allows consumers to check them. This
also enables sharing error classes between modules.
`js`
if (error instanceof InputError) {
// ...
}
ErrorClass.subclass() returns a
subclass.
Parent classes' options are merged with their subclasses.
`js
export const BaseError = ModernError.subclass('BaseError', {
props: { isError: true },
})
export const InputError = BaseError.subclass('InputError', {
props: { isUserError: true },
})
const error = new InputError('...')
console.log(error.isError) // true
console.log(error.isUserError) // true
console.log(error instanceof BaseError) // true
console.log(error instanceof InputError) // true
`
`js`
const InputError = BaseError.subclass('InputError', {
props: { isUserError: true },
})
const error = new InputError('...')
console.log(error.isUserError) // true
`js`
const error = new InputError('...', { props: { isUserError: true } })
console.log(error.isUserError) // true
Error properties that are internal or secret can be prefixed with _. This
makes them
non-enumerable,
which prevents iterating or logging them.
`jsuserId
const error = new InputError('...', {
props: { userId: 6, _isUserError: true },
})
console.log(error.userId) // 6
console.log(error._isUserError) // true
console.log(Object.keys(error)) // ['userId']
console.log(error) // is logged, but not _isUserError`
`js`
throw new InputError('Missing file path.')
Any error's message, class and
options can be wrapped using the
standard
cause option.
Instead of being set as a cause property, the inner error is directlymessage
merged to the outer error,
including its,stack,name,AggregateError.errors
and any additional property.
`js`
try {
// ...
} catch (cause) {
throw new InputError('Could not read the file.', { cause })
}
The outer error message is appended, unless it is empty. If the outer error
message ends with : or :\n, it is prepended instead.
`js`
const cause = new InputError('File does not exist.')
// InputError: File does not exist.
throw new InputError('', { cause })
`js`
// InputError: File does not exist.
// Could not read the file.
throw new InputError('Could not read the file.', { cause })
`jsCould not read the file:
// InputError: Could not read the file: File does not exist.
throw new InputError(, { cause })`
`jsCould not read the file:\n
// InputError: Could not read the file:
// File does not exist.
throw new InputError(, { cause })`
The outer error's class replaces the inner one.
`js`
try {
throw new AuthError('...')
} catch (cause) {
// Now an InputError
throw new InputError('...', { cause })
}
Except when the outer error's class is a parent class, such as
BaseError.
`js`
try {
throw new AuthError('...')
} catch (cause) {
// Still an AuthError
throw new BaseError('...', { cause })
}
The outer error's props and
plugin options are merged.
`jsouterOptions
try {
throw new AuthError('...', innerOptions)
} catch (cause) {
// are merged with innerOptions`
throw new BaseError('...', { ...outerOptions, cause })
}
The errors option aggregates multiple errors into one. This
is like
new AggregateError(errors)
except that it works with any error class.
`js`
const databaseError = new DatabaseError('...')
const authError = new AuthError('...')
throw new InputError('...', { errors: [databaseError, authError] })
// InputError: ... {
// [errors]: [
// DatabaseError: ...
// AuthError: ...
// ]
// }
Any error can be directly passed to the cause or
errors option, even if it is invalid,
unknown or not
normalized.
`js`
try {
// ...
} catch (cause) {
throw new InputError('...', { cause })
}
Manipulating errors that are not
Error instances
or that have
invalid properties
can lead to unexpected bugs.
BaseError.normalize() fixes that.
`jsinvalidError.message
try {
throw 'Missing file path.'
} catch (invalidError) {
// This fails: is undefined`
console.log(invalidError.message.trim())
}
`jsnormalizedError
try {
throw 'Missing file path.'
} catch (invalidError) {
const normalizedError = BaseError.normalize(invalidError)
// This works: 'Missing file path.'
// is a BaseError instance.`
console.log(normalizedError.message.trim())
}
Known errors should be handled in a try {} catch {} block and
wrapped with a specific class.
That block should only cover the statement that might throw in order to prevent
catching other unrelated errors.
`jsInputError
try {
return regExp.test(value)
} catch (error) {
// Now an instance`
throw new InputError('Invalid regular expression:', { cause: error })
}
If an error is not handled as described above, it is
considered _unknown_. This indicates an unexpected exception, usually a bug.
BaseError.normalize(error, UnknownError)
assigns the UnknownError class to those errors.
`js`
export const UnknownError = BaseError.subclass('UnknownError')
`jsUnknownError
try {
return regExp.test(value)
} catch (error) {
// Now an instance`
throw BaseError.normalize(error, UnknownError)
}
Wrapping a module's main functions with
BaseError.normalize(error, UnknownError)
ensures every error being thrown is valid, applies
plugins, and has a class that is either
_known_ or UnknownError.
`js`
export const main = () => {
try {
// ...
} catch (error) {
throw BaseError.normalize(error, UnknownError)
}
}
Plugins extend modern-errors features. All available plugins are
listed here.
To use a plugin, please install it, then pass it to the
plugins option.
`bash`
npm install modern-errors-{pluginName}
`js
import ModernError from 'modern-errors'
import modernErrorsBugs from 'modern-errors-bugs'
import modernErrorsSerialize from 'modern-errors-serialize'
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsBugs, modernErrorsSerialize],
})
// ...
`
Please see the following documentation to create your own
plugin.
Most plugins can be configured with options. The option's name is the same as
the plugin.
`jsmodern-errors-bugs
const options = {
// optionsprops
bugs: 'https://github.com/my-name/my-project/issues',
// can be configured and modified like plugin options`
props: { userId: 5 },
}
Plugin options can apply to (in priority order):
- Any error: second argument to ModernError.subclass()
`js`
export const BaseError = ModernError.subclass('BaseError', options)
- Any error of a specific class (and its subclasses): second argument to
ErrorClass.subclass()
`js`
export const InputError = BaseError.subclass('InputError', options)
- A specific error: second argument to new ErrorClass()
`js`
throw new InputError('...', options)
- A plugin method call: last argument, passing only that plugin's options
`js`
ErrorClassmethodName
`js`
errormethodName
The custom option can be used to provide an error classconstructor
with additional methods, , properties or options.
`jsclass
export const InputError = BaseError.subclass('InputError', {
// The must extend from the parent error classconstructor
custom: class extends BaseError {
// If a is defined, its parameters must be (message, options)options
// Additional can be defined.
constructor(message, options) {
message += options?.suffix ?? ''
super(message, options)
}
isUserInput() {
// ...
}
},
})
const error = new InputError('Wrong user name', { suffix: ': example' })
console.log(error.message) // 'Wrong user name: example'
console.log(error.isUserInput())
`
Please see the following documentation for information
about TypeScript types.
Top-level ErrorClass.
name: string\options: ClassOptions?
Creates and returns a child ErrorClass.
#### options.props
_Type_: object
#### options.plugins
_Type_: [Plugin[]](#-plugins)
#### options.custom
_Type_: class extends ErrorClass {}
Custom class to add any methods, constructor or properties.
#### options.\*
Any plugin options can also be specified.
message: string\options: InstanceOptions?\Error
_Return value_:
#### options.props
_Type_: object
#### options.cause
_Type_: any
Inner error being wrapped.
#### options.errors
_Type_: any[]
Array of errors being aggregated.
#### options.\*
Any plugin options can also be specified.
error: Error | any\NewErrorClass: subclass of ErrorClass\Error
_Return value_:
Normalizes invalid errors.
If error is an instance of ErrorClass (or one of its subclasses), it is leftNewErrorClass
as is. Otherwise, it is
converted to , which defaults toErrorClass itself.
This framework brings together a collection of modules which can also be used
individually:
- error-custom-class: Create
one error class
- error-class-utils: Utilities
to properly create error classes
- error-serializer: Convert
errors to/from plain objects
- normalize-exception:
Normalize exceptions/errors
- is-error-instance: Check if
a value is an Error instancemerge-error-cause
- : Merge ancause
error with its set-error-class
- : Properlyset-error-message
update an error's class
- : Properlywrap-error-message
update an error's message
- :set-error-props
Properly wrap an error's message
- : Properlyset-error-stack
update an error's properties
- : Properlyhandle-cli-error
update an error's stack
- : 💣 Errorbeautiful-error
handler for CLI applications 💥
- : Prettifylog-process-errors
error messages and stacks
- : Showerror-http-response
some ❤ to Node.js process errors
- :winston-error-format
Create HTTP error responses
- : Log
errors with Winston
For any question, _don't hesitate_ to submit an issue on GitHub.
Everyone is welcome regardless of personal background. We enforce a
Code of conduct in order to promote a positive and
inclusive environment.
This project was made with ❤️. The simplest way to give back is by starring and
sharing it online.
If the documentation is unclear or has a typo, please click on the page's Edit`
button (pencil icon) and suggest a correction.
If you would like to help us fix a bug or add a new feature, please check our
guidelines. Pull requests are welcome!
ehmicky 💻 🎨 🤔 📖 | const_var 🤔 💬 | Andy Brenneke 🤔 💬 | Graham Fisher 🐛 | renzor 💬 🤔 | Eugene 💻 🐛 | Jonathan Chambers ⚠️ 🐛 |
heyhey123 🐛 | Benjamin Kroeger 🐛 |