`modern-errors` plugin to serialize/parse errors
npm install modern-errors-serialize







modern-errors
plugin to serialize/parse
errors.
This adds BaseError.serialize() andBaseError.parse() to serialize/parse errors
to/from plain objects.
- Ensures errors are safe to serialize with JSON
- Deep serialization/parsing
- Custom serialization/parsing (e.g. YAML or
process.send())
- Keeps error classes
- Preserves errors' additional properties
- Works recursively with
AggregateError
- Safe: this never throws
Adding the plugin tomodern-errors.
``js
import ModernError from 'modern-errors'
import modernErrorsSerialize from 'modern-errors-serialize'
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsSerialize],
})
// ...
`
Serializing errors to plain objects.
`js
const error = new ExampleError('message', { props: { filePath } })
const errorObject = BaseError.serialize(error)
// { name: 'ExampleError', message: 'message', stack: '...', filePath: '...' }
const errorString = JSON.stringify(errorObject)
// '{"name":"ExampleError",...}'
`
Parsing errors from plain objects.
`js`
const newErrorObject = JSON.parse(errorString)
const newError = BaseError.parse(newErrorObject)
// ExampleError: message
// at ...
// filePath: '...'
`bash`
npm install modern-errors-serialize
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.
_Type_: Plugin
Plugin object to pass to the
plugins option of
ErrorClass.subclass().
error: ErrorInstance\ErrorObject
_Return value_:
Converts error to an error plain object. All
error properties
are kept.
Plugin options are
also preserved.
errorObject: ErrorObject\ErrorInstance
_Return value_:
Converts errorObject to an error instance. The original error classes areBaseError
preserved providing they are
subclasses of.
_Type_: object
_Type_: boolean\false
_Default_:
Unless this option is true, nested errors are also serialized/parsed. They can
be inside other errors, plain objects or arrays.
`js
const inner = new ExampleError('inner')
const error = new ExampleError('example', { props: { inner } })
BaseError.serialize(error).inner // { name: 'BaseError', message: 'inner', ... }
BaseError.serialize(error, { shallow: true }).inner // BaseError
const errorObject = BaseError.serialize(error)
BaseError.parse(errorObject).inner // BaseError
BaseError.parse(errorObject, { shallow: true }).inner // { name: '...', ... }
`
_Type_: boolean\false
_Default_:
By default, when the argument is not an Error instance or an error plaintrue
object, it is converted to one. If this option is , it is kept as is
instead.
`js
BaseError.serialize('example') // { name: 'BaseError', message: 'example', ... }
BaseError.serialize('example', { loose: true }) // 'example'
BaseError.parse('example') // BaseError
BaseError.parse('example', { loose: true }) // 'example'
`
#### include
_Type_: string[]
During serialization, only pick
specific properties.
`js`
BaseError.serialize(error, { include: ['message'] }) // { message: 'example' }
#### exclude
_Type_: string[]
During serialization, omit
specific properties.
`js`
BaseError.serialize(error, { exclude: ['stack'] }) // { name: 'Error', message: 'example' }
_Type_: (errorObject, errorInstance) => void
During serialization, transform
each error plain object.
errorObject is the error after serialization. It must be directly mutated.
errorInstance is the error before serialization.
_Type_: (constructorArgs, errorObject, ErrorClass) => void
During parsing, transform the
arguments passed to each new Error().
constructorArgs is the array of arguments. Usually, constructorArgs[0] isconstructorArgs[1]
the
error message
and is theconstructorArgs
constructor options object. must be directly mutated.
errorObject is the error before parsing. ErrorClass is its class.
_Type_: (errorInstance, errorObject) => void
During parsing, transform each
Error instance.
errorInstance is the error after parsing. It must be directly mutated.
errorObject is the error before parsing.
Options can apply to (in priority order):
- Any error: second argument to
ModernError.subclass()
`js`
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsSerialize],
serialize: options,
})
- Any error of a specific class (and its subclasses): second argument to
ErrorClass.subclass()
`js`
export const ExampleError = BaseError.subclass('ExampleError', {
serialize: options,
})
- A specific error: second argument to
new ErrorClass()
`js`
throw new ExampleError('...', { serialize: options })
- A specific BaseError.serialize(error) or
BaseError.parse(errorObject) call
`js`
BaseError.serialize(error, options)
`js`
BaseError.parse(errorObject, options)
Error plain objects are always
safe to serialize with JSON.
`js
const error = new ExampleError('message')
error.cycle = error
// Cycles make JSON.stringify() throw, so they are removed`
console.log(BaseError.serialize(error).cycle) // undefined
The loose option can be used to deeply serialize/parse objects and
arrays.
`js
const error = new ExampleError('message')
const deepArray = BaseError.serialize([{}, { error }], { loose: true })
const jsonString = JSON.stringify(deepArray)
const newDeepArray = JSON.parse(jsonString)
const newError = BaseError.parse(newDeepArray, { loose: true })[1].error
// ExampleError: message
// at ...
`
error.toJSON()
is defined. It is automatically called by
JSON.stringify().
`js
const error = new ExampleError('message')
const deepArray = [{}, { error }]
const jsonString = JSON.stringify(deepArray)
const newDeepArray = JSON.parse(jsonString)
const newError = BaseError.parse(newDeepArray, { loose: true })[1].error
// ExampleError: message
// at ...
`
`js
const ExampleError = BaseError.subclass('ExampleError', {
serialize: { include: ['name', 'message', 'stack'] },
})
const error = new ExampleError('example')
error.prop = true
const errorObject = ExampleError.serialize(error)
console.log(errorObject.prop) // undefined
console.log(errorObject) // { name: 'Error', message: 'example', stack: '...' }
`
`js
const ExampleError = BaseError.subclass('ExampleError', {
serialize: { exclude: ['stack'] },
})
const error = new ExampleError('example')
const errorObject = ExampleError.serialize(error)
console.log(errorObject.stack) // undefined
console.log(errorObject) // { name: 'Error', message: 'example' }
`
`js
const errors = [new ExampleError('message secret')]
errors[0].date = new Date()
const errorObjects = BaseError.serialize(errors, {
loose: true,
// Serialize Date instances as strings
transformObject: (errorObject) => {
errorObject.date = errorObject.date.toString()
},
})
console.log(errorObjects[0].date) // Date string
const newErrors = BaseError.parse(errorObjects, {
loose: true,
// Transform error message
transformArgs: (constructorArgs) => {
constructorArgs[0] = constructorArgs[0].replace('secret', '*')
},
// Parse date strings as Date instancesDate
transformInstance: (error) => {
error.date = new Date(error.date)
},
})
console.log(newErrors[0].message) // 'message *'
console.log(newErrors[0].date) // instance`
Errors are converted to/from plain objects, not strings. This allows any
serialization/parsing logic to be performed.
`js
import { dump, load } from 'js-yaml'
const error = new ExampleError('message')
const errorObject = BaseError.serialize(error)
const errorYamlString = dump(errorObject)
// name: ExampleError
// message: message
// stack: ExampleError: message ...
const newErrorObject = load(errorYamlString)
const newError = BaseError.parse(newErrorObject) // ExampleError: message
`
`js`
const error = new ExampleError('message', { props: { prop: true } })
const errorObject = BaseError.serialize(error)
console.log(errorObject.prop) // true
const newError = BaseError.parse(errorObject)
console.log(newError.prop) // true
`js
const error = new ExampleError('message', {
errors: [new ExampleError('one'), new ExampleError('two')],
})
const errorObject = BaseError.serialize(error)
// {
// name: 'ExampleError',
// message: 'message',
// stack: '...',
// errors: [{ name: 'ExampleError', message: 'one', stack: '...' }, ...],
// }
const newError = BaseError.parse(errorObject)
// ExampleError: message
// [errors]: [ExampleError: one, ExampleError: two]
`
If an error with a
custom class is
parsed, its custom constructor is not called. However, any property previously
set by that constructor is still preserved, providing it is serializable and
enumerable.
`js
const ExampleError = BaseError.subclass('ExampleError', {
custom: class extends BaseError {
constructor(message, options, prop) {
super(message, options, prop)
this.prop = prop
}
},
})
const error = new ExampleError('message', {}, true)
const errorObject = BaseError.serialize(error)
// constructor(message, options, prop) is not calledconstructor(...)
const newError = BaseError.parse(errorObject)
// But properties set by that are kept`
console.log(newError.prop) // true
- error-serializer: Convert
errors to/from plain objects
- modern-errors: Handle errors in
a simple, stable, consistent way
- modern-errors-cli: Handle
errors in CLI modules
- modern-errors-process:
Handle process errors
- modern-errors-bugs: Print
where to report bugs
- 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
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 💻 🎨 🤔 📖 | Benjamin Kroeger 🤔 |