Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ npm i @fastify/error

The module exports a function that you can use for consistent error objects, it takes 4 parameters:

```
```js
createError(code, message [, statusCode [, Base [, captureStackTrace]]])
```

Expand Down Expand Up @@ -58,6 +58,83 @@ new CustomError('world')
new CustomError(1)
```

### instanceof

All errors created with `createError` will be instances of the base error constructor you provided, or `Error` if none was provided.

```js
const createError = require('@fastify/error')
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
const customError = new CustomError('world')

console.log(customError instanceof CustomError) // true
console.log(customError instanceof TypeError) // true
console.log(customError instanceof Error) // true
```

All instantiated errors will be instances of the `FastifyError` class. The `FastifyError` class can be required from the module directly.

```js
const { createError, FastifyError } = require('@fastify/error')
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
const customError = new CustomError('world')

console.log(customError instanceof FastifyError) // true
```

It is possible to create a `FastifyError` that extends another `FastifyError`, created by `createError`, while instanceof working correctly.

```js
const { createError, FastifyError } = require('@fastify/error')

const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
const ChildCustomError = createError('CHILD_ERROR_CODE', 'Hello %s', 500, CustomError)

const customError = new ChildCustomError('world')

console.log(customError instanceof ChildCustomError) // true
console.log(customError instanceof CustomError) // true
console.log(customError instanceof FastifyError) // true
console.log(customError instanceof TypeError) // true
console.log(customError instanceof Error) // true
```

If `fastify-error` is installed multiple times as direct and/or transitive dependency, it is ensured that Errors created with `createError` are working with the `instanceof` operator correctly cross the `fastify-error` installation, as long the code, e.g. `FST_ERR_CUSTOM_ERROR`, is the same.

```js
const { createError, FastifyError } = require('@fastify/error')

// CustomError from `@fastify/some-plugin` is created with `createError` and
// has its own `@fastify/error` installation as dependency. CustomError has
// FST_ERR_CUSTOM_ERROR as code.
const { CustomError: CustomErrorFromPlugin } = require('@fastify/some-plugin')

const CustomError = createError('FST_ERR_CUSTOM_ERROR', 'Hello %s', 500)

const customError = new CustomError('world')
const customErrorFromPlugin = new CustomErrorFromPlugin('world')

console.log(customError instanceof CustomError) // true
console.log(customError instanceof CustomErrorFromPlugin) // true
console.log(customErrorFromPlugin instanceof CustomError) // true
console.log(customErrorFromPlugin instanceof CustomErrorFromPlugin) // true
```

Changing the code of an instantiated Error will not change the result of the `instanceof` operator.

```js
const { createError, FastifyError } = require('@fastify/error')

const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
const AnotherCustomError = createError('ANOTHER_ERROR_CODE', 'Hello %s', 500, CustomError)

const customError = new CustomError('world')
customError.code = 'ANOTHER_ERROR_CODE'

console.log(customError instanceof CustomError) // true
console.log(customError instanceof AnotherCustomError) // false
```

## License

Licensed under [MIT](./LICENSE).
45 changes: 45 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@ function toString () {
return `${this.name} [${this.code}]: ${this.message}`
}

const FastifyGenericErrorSymbol = Symbol.for('fastify-error-generic')

function createError (code, message, statusCode = 500, Base = Error, captureStackTrace = createError.captureStackTrace) {
const shouldCreateFastifyGenericError = code === FastifyGenericErrorSymbol

if (shouldCreateFastifyGenericError) {
code = 'FST_ERR'
}

if (!code) throw new Error('Fastify error code must not be empty')
if (!message) throw new Error('Fastify error message must not be empty')

code = code.toUpperCase()
!statusCode && (statusCode = undefined)

const FastifySpecificErrorSymbol = Symbol.for(`fastify-error ${code}`)

function FastifyError (...args) {
if (!new.target) {
return new FastifyError(...args)
Expand All @@ -38,9 +48,41 @@ function createError (code, message, statusCode = 500, Base = Error, captureStac
enumerable: false,
writable: true,
configurable: true
},
[FastifyGenericErrorSymbol]: {
value: true,
enumerable: false,
writable: false,
configurable: false
},
[FastifySpecificErrorSymbol]: {
value: true,
enumerable: false,
writable: false,
configurable: false
}
})

if (shouldCreateFastifyGenericError) {
Object.defineProperty(FastifyError, Symbol.hasInstance, {
value (instance) {
return instance && instance[FastifyGenericErrorSymbol]
},
configurable: false,
writable: false,
enumerable: false
})
} else {
Object.defineProperty(FastifyError, Symbol.hasInstance, {
value (instance) {
return instance && instance[FastifySpecificErrorSymbol]
},
configurable: false,
writable: false,
enumerable: false
})
}

FastifyError.prototype[Symbol.toStringTag] = 'Error'

FastifyError.prototype.toString = toString
Expand All @@ -50,6 +92,9 @@ function createError (code, message, statusCode = 500, Base = Error, captureStac

createError.captureStackTrace = true

const FastifyErrorConstructor = createError(FastifyGenericErrorSymbol, 'Fastify Error', 500, Error)

module.exports = createError
module.exports.FastifyError = FastifyErrorConstructor
module.exports.default = createError
module.exports.createError = createError
11 changes: 10 additions & 1 deletion test/index.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const test = require('node:test')
const createError = require('..')
const { createError, FastifyError } = require('..')

test('Create error with zero parameter', (t) => {
t.plan(6)
Expand Down Expand Up @@ -221,3 +221,12 @@ test('Create an error with last argument null', (t) => {
t.assert.ok(err instanceof Error)
t.assert.ifError(err.cause)
})

test('check if FastifyError is instantiable', (t) => {
t.plan(2)

const err = new FastifyError()

t.assert.ok(err instanceof FastifyError)
t.assert.ok(err instanceof Error)
})
Loading