• Stars
    star
    1,273
  • Rank 36,981 (Top 0.8 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Handle errors in a simple, stable, consistent way
modern-errors logo

Node Browsers TypeScript Codecov Minified size Mastodon Medium

Handle errors in a simple, stable, consistent way.

Features

Simple patterns to:

Stability:

Plugins

Example

Create error classes.

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.

throw new InputError('Invalid file path', { props: { filePath: '/...' } })

Wrap errors.

try {
  // ...
} catch (cause) {
  throw new InputError('Could not read the file.', { cause })
}

Normalize errors.

try {
  throw 'Missing file path.'
} catch (error) {
  // Normalized from a string to a `BaseError` instance
  throw BaseError.normalize(error)
}

Use plugins.

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))

Install

npm install modern-errors

If any plugin is used, it must also be installed.

npm install modern-errors-{pluginName}

This package works in both Node.js >=16.17.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.

Usage

⛑️ Error classes

Create error classes

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')

Export error classes

Exporting and documenting all error classes allows consumers to check them. This also enables sharing error classes between modules.

Check error classes

if (error instanceof InputError) {
  // ...
}

Error subclasses

ErrorClass.subclass() returns a subclass. Parent classes' options are merged with their subclasses.

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

🏷️ Error properties

Error class properties

const InputError = BaseError.subclass('InputError', {
  props: { isUserError: true },
})
const error = new InputError('...')
console.log(error.isUserError) // true

Error instance properties

const error = new InputError('...', { props: { isUserError: true } })
console.log(error.isUserError) // true

Internal error properties

Error properties that are internal or secret can be prefixed with _. This makes them non-enumerable, which prevents iterating or logging them.

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) // `userId` is logged, but not `_isUserError`

🎀 Wrap errors

Throw errors

throw new InputError('Missing file path.')

Wrap inner error

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 directly merged to the outer error, including its message, stack, name, AggregateError.errors and any additional property.

try {
  // ...
} catch (cause) {
  throw new InputError('Could not read the file.', { cause })
}

Wrap error message

The outer error message is appended, unless it is empty. If the outer error message ends with : or :\n, it is prepended instead.

const cause = new InputError('File does not exist.')
// InputError: File does not exist.
throw new InputError('', { cause })
// InputError: File does not exist.
// Could not read the file.
throw new InputError('Could not read the file.', { cause })
// InputError: Could not read the file: File does not exist.
throw new InputError(`Could not read the file:`, { cause })
// InputError: Could not read the file:
// File does not exist.
throw new InputError(`Could not read the file:\n`, { cause })

Wrap error class

The outer error's class replaces the inner one.

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.

try {
  throw new AuthError('...')
} catch (cause) {
  // Still an AuthError
  throw new BaseError('...', { cause })
}

Wrap error options

The outer error's props and plugin options are merged.

try {
  throw new AuthError('...', innerOptions)
} catch (cause) {
  // `outerOptions` are merged with `innerOptions`
  throw new BaseError('...', { ...outerOptions, cause })
}

Aggregate errors

The errors option aggregates multiple errors into one. This is like new AggregateError(errors) except that it works with any error class.

const databaseError = new DatabaseError('...')
const authError = new AuthError('...')
throw new InputError('...', { errors: [databaseError, authError] })
// InputError: ... {
//   [errors]: [
//     DatabaseError: ...
//     AuthError: ...
//   ]
// }

🚨 Normalize errors

Wrapped errors

Any error can be directly passed to the cause or errors option, even if it is invalid, unknown or not normalized.

try {
  // ...
} catch (cause) {
  throw new InputError('...', { cause })
}

Invalid errors

Manipulating errors that are not Error instances or that have invalid properties can lead to unexpected bugs. BaseError.normalize() fixes that.

try {
  throw 'Missing file path.'
} catch (invalidError) {
  // This fails: `invalidError.message` is `undefined`
  console.log(invalidError.message.trim())
}
try {
  throw 'Missing file path.'
} catch (invalidError) {
  const normalizedError = BaseError.normalize(invalidError)
  // This works: 'Missing file path.'
  // `normalizedError` is a `BaseError` instance.
  console.log(normalizedError.message.trim())
}

🐞 Unknown errors

Handling known errors

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.

try {
  return regExp.test(value)
} catch (error) {
  // Now an `InputError` instance
  throw new InputError('Invalid regular expression:', { cause: error })
}

Normalizing unknown errors

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.

export const UnknownError = BaseError.subclass('UnknownError')
try {
  return regExp.test(value)
} catch (error) {
  // Now an `UnknownError` instance
  throw BaseError.normalize(error, UnknownError)
}

Top-level error handler

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.

export const main = () => {
  try {
    // ...
  } catch (error) {
    throw BaseError.normalize(error, UnknownError)
  }
}

🔌 Plugins

List of plugins

Plugins extend modern-errors features. All available plugins are listed here.

Adding plugins

To use a plugin, please install it, then pass it to the plugins option.

npm install modern-errors-{pluginName}
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],
})
// ...

Custom plugins

Please see the following documentation to create your own plugin.

Plugin options

Most plugins can be configured with options. The option's name is the same as the plugin.

const options = {
  // `modern-errors-bugs` options
  bugs: 'https://github.com/my-name/my-project/issues',
  // `props` can be configured and modified like plugin options
  props: { userId: 5 },
}

Plugin options can apply to (in priority order):

export const BaseError = ModernError.subclass('BaseError', options)
export const InputError = BaseError.subclass('InputError', options)
throw new InputError('...', options)
  • A plugin method call: last argument, passing only that plugin's options
ErrorClass[methodName](...args, options[pluginName])
error[methodName](...args, options[pluginName])

🔧 Custom logic

The custom option can be used to provide an error class with additional methods, constructor, properties or options.

export const InputError = BaseError.subclass('InputError', {
  // The `class` must extend from the parent error class
  custom: class extends BaseError {
    // If a `constructor` is defined, its parameters must be (message, options)
    // Additional `options` 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())

🤓 TypeScript

Please see the following documentation for information about TypeScript types.

API

ModernError

Top-level ErrorClass.

ErrorClass.subclass(name, options?)

name: string
options: ClassOptions?

Creates and returns a child ErrorClass.

options

options.props

Type: object

Error class properties.

options.plugins

Type: Plugin[]

options.custom

Type: class extends ErrorClass {}

Custom class to add any methods, constructor or properties.

options.*

Any plugin options can also be specified.

new ErrorClass(message, options?)

message: string
options: InstanceOptions?
Return value: Error

options

options.props

Type: object

Error instance properties.

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.

ErrorClass.normalize(error, NewErrorClass?)

error: Error | any
NewErrorClass: subclass of ErrorClass
Return value: Error

Normalizes invalid errors.

If the error's class is a subclass of ErrorClass, it is left as is. Otherwise, it is converted to NewErrorClass, which defaults to ErrorClass itself.

Modules

This framework brings together a collection of modules which can also be used individually:

Support

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.

Contributing

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
ehmicky

💻 🎨 🤔 📖
const_var
const_var

🤔 💬
Andy Brenneke
Andy Brenneke

🤔 💬
Graham Fisher
Graham Fisher

🐛
renzor
renzor

💬 🤔
Eugene
Eugene

💻 🐛

More Repositories

1

cross-platform-node-guide

📗 How to write cross-platform Node.js code
JavaScript
1,331
star
2

nve

Run any command on specific Node.js versions
JavaScript
614
star
3

wild-wild-path

🤠 Object property paths with wildcards and regexps 🌵
JavaScript
608
star
4

log-process-errors

Show some ❤️ to Node.js process errors
JavaScript
471
star
5

human-signals

Human-friendly process signals
JavaScript
261
star
6

autoserver

Create a full-featured REST/GraphQL API from a configuration file
JavaScript
200
star
7

safe-json-value

⛑️ JSON serialization should never fail
JavaScript
198
star
8

cross-platform-terminal-characters

All the characters that work on most terminals
JavaScript
196
star
9

unix-permissions

Swiss Army knife for Unix permissions
JavaScript
119
star
10

test-each

🤖 Repeat tests. Repeat tests. Repeat tests.
JavaScript
103
star
11

Notes

Technologies I've learned
sed
58
star
12

gulp-execa

Gulp.js command execution for humans
JavaScript
55
star
13

fast-cartesian

Fast cartesian product
TypeScript
53
star
14

get-bin-path

Get the current package's binary path
JavaScript
34
star
15

wild-wild-utils

🤠 Functional utilities using object property paths with wildcards and regexps 🌵
JavaScript
28
star
16

get-node

Download a specific version of Node.js
JavaScript
19
star
17

handle-cli-error

💣 Error handler for CLI applications 💥
JavaScript
15
star
18

keep-func-props

Wrap a function without changing its name and other properties
JavaScript
13
star
19

node-version-alias

Resolve Node.js version aliases like `latest`, `lts` or `erbium`
JavaScript
12
star
20

big-cartesian

Cartesian product for big inputs
TypeScript
12
star
21

eslint-config

ESLint configuration for my own projects
JavaScript
10
star
22

preferred-node-version

Get the preferred Node.js version of a project or user
JavaScript
9
star
23

all-node-versions

List all available Node.js versions
JavaScript
9
star
24

error-serializer

Convert errors to/from plain objects.
JavaScript
9
star
25

modern-errors-http

`modern-errors` plugin to create HTTP error responses.
TypeScript
8
star
26

normalize-node-version

Normalize and validate Node.js versions
JavaScript
8
star
27

cv-website

Static page with my CV website
HTML
8
star
28

truncate-json

Truncate a JSON string.
JavaScript
8
star
29

merge-error-cause

Merge an error with its `cause`
JavaScript
7
star
30

get-node-cli

Download a specific version of Node.js (CLI)
JavaScript
7
star
31

global-cache-dir

Get the global cache directory
JavaScript
6
star
32

dev-tasks

Automated development tasks for my own projects
Shell
5
star
33

modern-errors-serialize

`modern-errors` plugin to serialize/parse errors.
JavaScript
5
star
34

portuguese-conjugation-cheat-sheet

Portuguese conjugation cheat sheet
CSS
5
star
35

spyd

Complete yet simple benchmark runner
JavaScript
5
star
36

string-byte-length

Get the UTF-8 byte length of a string.
JavaScript
5
star
37

template-javascript

JavaScript library template
JavaScript
5
star
38

colors-option

Let users toggle colors
JavaScript
5
star
39

modern-errors-winston

`modern-errors` plugin for Winston.
JavaScript
5
star
40

normalize-exception

Normalize exceptions/errors
JavaScript
5
star
41

abstract-parser

Abstraction layer for JavaScript parsers
JavaScript
4
star
42

modern-errors-cli

`modern-errors` plugin to handle errors in CLI modules.
TypeScript
4
star
43

error-custom-class

Create custom error classes
JavaScript
4
star
44

fetch-node-website

Fetch releases on nodejs.org
JavaScript
4
star
45

winston-error-format

Log errors with Winston
JavaScript
4
star
46

is-json-value

Check if a value is valid JSON.
JavaScript
4
star
47

error-cause-polyfill

Polyfill `error.cause`
JavaScript
4
star
48

modern-errors-switch

`modern-errors` plugin to execute class-specific logic.
JavaScript
4
star
49

modern-errors-clean

`modern-errors` plugin to clean stack traces.
JavaScript
4
star
50

wild-wild-parser

🤠 Parser for object property paths with wildcards and regexps 🌵
JavaScript
4
star
51

precise-now

Like `performance.now()` but in nanoseconds
TypeScript
4
star
52

error-http-response

Create HTTP error responses.
JavaScript
3
star
53

test-api

[WIP] Automated API testing
JavaScript
3
star
54

modern-errors-process

`modern-errors` plugin to handle process errors.
JavaScript
3
star
55

guess-json-indent

Guess the indentation of a JSON string.
JavaScript
3
star
56

time-resolution

Find the process's time resolution
TypeScript
3
star
57

is-error-instance

Check if a value is an `Error` instance.
TypeScript
3
star
58

declarative-merge

Merge objects/arrays declaratively
JavaScript
3
star
59

template-typescript

TypeScript library template
TypeScript
3
star
60

modern-errors-bugs

`modern-errors` plugin to print where to report bugs.
JavaScript
3
star
61

set-error-class

Properly update an error's class.
JavaScript
2
star
62

dev-parser

Parse JavaScript using a terminal
JavaScript
2
star
63

ehmicky

Node.js back-end developer
2
star
64

set-error-stack

Properly update an error's stack.
JavaScript
2
star
65

set-array

Set array items declaratively
JavaScript
2
star
66

set-error-message

Properly update an error's message.
JavaScript
2
star
67

set-error-props

Properly update an error's properties
JavaScript
2
star
68

string-byte-slice

Like `string.slice()` but bytewise.
JavaScript
2
star
69

redefine-property

Better `Object.defineProperty()`
JavaScript
2
star
70

terminal-theme

🎨 Use a color theme for your code's terminal output
JavaScript
2
star
71

wrap-error-message

Properly wrap an error's message.
JavaScript
2
star
72

chalk-string

Chalk with style strings.
JavaScript
2
star
73

error-class-utils

Properly create error classes.
JavaScript
2
star
74

create-error-types

Create multiple error types.
JavaScript
1
star
75

oh-oh

This is an.
JavaScript
1
star
76

design

Logos of my projects
1
star
77

prettier-config

Prettier configuration for my own projects
JavaScript
1
star