{"id":13847293,"url":"https://github.com/theogravity/new-error","last_synced_at":"2025-04-10T05:24:00.819Z","repository":{"id":44716633,"uuid":"263199208","full_name":"theogravity/new-error","owner":"theogravity","description":"Production-grade error creation and serialization library designed for Typescript","archived":false,"fork":false,"pushed_at":"2023-06-14T19:09:50.000Z","size":1282,"stargazers_count":23,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-14T00:59:06.958Z","etag":null,"topics":["api","creation","custom","errors","generation","inherit","logging","nodejs","serializer","stack","trace","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/theogravity.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-05-12T01:19:42.000Z","updated_at":"2023-09-08T18:07:20.000Z","dependencies_parsed_at":"2024-01-15T20:49:13.319Z","dependency_job_id":"337ddac5-4b99-4e45-bde2-c7f10c617610","html_url":"https://github.com/theogravity/new-error","commit_stats":{"total_commits":119,"total_committers":4,"mean_commits":29.75,"dds":"0.17647058823529416","last_synced_commit":"9dc75ac589ec73262012e25601f4e38d76b31426"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":"theogravity/boilerplate-typescript-old","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fnew-error","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fnew-error/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fnew-error/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fnew-error/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/theogravity","download_url":"https://codeload.github.com/theogravity/new-error/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248161739,"owners_count":21057643,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","creation","custom","errors","generation","inherit","logging","nodejs","serializer","stack","trace","typescript"],"created_at":"2024-08-04T18:01:15.881Z","updated_at":"2025-04-10T05:24:00.792Z","avatar_url":"https://github.com/theogravity.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# new-error\n\n[![NPM version](https://img.shields.io/npm/v/new-error.svg?style=flat-square)](https://www.npmjs.com/package/new-error)\n[![CircleCI](https://circleci.com/gh/theogravity/new-error.svg?style=svg)](https://circleci.com/gh/theogravity/new-error) \n![built with typescript](https://camo.githubusercontent.com/92e9f7b1209bab9e3e9cd8cdf62f072a624da461/68747470733a2f2f666c61742e62616467656e2e6e65742f62616467652f4275696c74253230576974682f547970655363726970742f626c7565) \n[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)\n\nA production-grade error creation library designed for Typescript. Useful for direct printing\nof errors to a client or for internal development / logs.\n\n- All created errors extend `Error` with additional methods added on.\n- Show errors that are safe for client / user-consumption vs internal only.\n- Create your own custom error types with custom messaging, status codes and metadata.\n  * Errors can be created via a registry (recommended), or you can create your own error classes.\n- Attach an error to your error object to get the full error chain.\n- Selectively expose error metadata based on internal or external use.\n- Built-in auto-completion for Typescript when searching for registered error types.\n- 100% test coverage\n\n![Generating an error with autocompletion](/autocomplete.jpg?raw=true \"Title\")\n\n# Table of Contents \n\n\u003c!-- TOC --\u003e\n\n- [Motivation / Error handling use-cases](#motivation--error-handling-use-cases)\n- [Installation](#installation)\n- [Examples](#examples)\n  - [With the error registry](#with-the-error-registry)\n    - [Helper utilities](#helper-utilities)\n      - [Auto-generate high level error properties](#auto-generate-high-level-error-properties)\n        - [Configuration options](#configuration-options)\n      - [Auto-generate low level error properties](#auto-generate-low-level-error-properties)\n        - [Configuration options](#configuration-options-1)\n  - [Class-based with low level errors without a registry](#class-based-with-low-level-errors-without-a-registry)\n  - [Bare-bones class-based error](#bare-bones-class-based-error)\n- [Example Express Integration](#example-express-integration)\n- [Working with log levels](#working-with-log-levels)\n- [Error Registry API](#error-registry-api)\n  - [Constructor](#constructor)\n    - [Configuration options](#configuration-options-2)\n    - [Example](#example)\n  - [Child registry with context](#child-registry-with-context)\n    - [Configuration options](#configuration-options-3)\n    - [Example](#example-1)\n  - [Creating errors](#creating-errors)\n    - [Create a well-defined error](#create-a-well-defined-error)\n    - [Create an error without a low-level error](#create-an-error-without-a-low-level-error)\n      - [Specify a custom message](#specify-a-custom-message)\n      - [Use the message property from the high level error if defined](#use-the-message-property-from-the-high-level-error-if-defined)\n      - [Custom message not defined and high level error has no message property defined](#custom-message-not-defined-and-high-level-error-has-no-message-property-defined)\n    - [Error creation handler](#error-creation-handler)\n  - [`instanceOf` / comparisons](#instanceof--comparisons)\n    - [Comparing a custom error](#comparing-a-custom-error)\n    - [Native `instanceof`](#native-instanceof)\n- [Error API](#error-api)\n  - [Constructor](#constructor-1)\n    - [Configuration options](#configuration-options-4)\n  - [Getters](#getters)\n  - [Basic setters](#basic-setters)\n  - [Static methods](#static-methods)\n  - [Utility methods](#utility-methods)\n  - [Set an error id](#set-an-error-id)\n  - [Set a request id](#set-a-request-id)\n  - [Attaching errors](#attaching-errors)\n    - [Append the attached error message to the main error message](#append-the-attached-error-message-to-the-main-error-message)\n  - [Format messages](#format-messages)\n  - [Converting the error into another type](#converting-the-error-into-another-type)\n    - [Apollo GraphQL example](#apollo-graphql-example)\n  - [Adding metadata](#adding-metadata)\n    - [Safe metadata](#safe-metadata)\n    - [Internal metadata](#internal-metadata)\n  - [Serializing errors](#serializing-errors)\n    - [Safe serialization](#safe-serialization)\n    - [Internal serialization](#internal-serialization)\n    - [Post-processing handlers](#post-processing-handlers)\n- [Deserialization](#deserialization)\n  - [Issues with deserialization](#issues-with-deserialization)\n    - [Deserialization is not perfect](#deserialization-is-not-perfect)\n    - [Potential security issues with deserialization](#potential-security-issues-with-deserialization)\n  - [`ErrorRegistry#fromJSON()` method](#errorregistryfromjson-method)\n  - [`static BaseError#fromJSON()` method](#static-baseerrorfromjson-method)\n  - [Stand-alone instance-based deserialization](#stand-alone-instance-based-deserialization)\n- [Looking for production-grade env variable / configuration management?](#looking-for-production-grade-env-variable--configuration-management)\n\n\u003c!-- TOC END --\u003e\n\n# Motivation / Error handling use-cases\n\nThe basic Javascript `Error` type is extremely bare bones - you can only specify a message.\n\nIn a production-level application, I've experienced the following use-cases:\n\n- A developer should be able to add metadata to the error that may assist with troubleshooting.\n- A developer should be able to reference the original error.\n- Errors should be able to work with a logging framework.\n- Errors should be well-formed / have a defined structure that can be consumed / emitted for analytics and services.\n- Errors should be able to be cross-referenced in various systems via an identifier / error id.\n- Errors should not expose sensitive data to the end-user / client.\n- Errors that are exposed to the end-user / client should not reveal data that would expose system internals.\n- Error responses from an API service should follow a common format.\n- End-users / clients should be able to relay the error back to support; the relayed data should be enough for a developer to troubleshoot.\n- Client developers prefer a list of error codes to expect from an API service so they can properly handle errors.\n- You want to classify the types of errors that your application is emitting in your metrics / analytics tool.\n\n`new-error` was built with these use-cases in mind.\n\n# Installation\n\n`$ npm i new-error --save`\n\n# Examples\n\n- Define a set of high level errors\n  * Common high level error types could be 4xx/5xx HTTP codes\n- Define a set of low level errors\n  * Think of low level errors as a fine-grained sub-code/category to a high level error\n- Initialize the error registry with the errors\n\n## With the error registry\n\nThe error registry is the fastest way to define and create errors.\n\n```typescript\n// This is a working example\nimport { ErrorRegistry } from 'new-error'\n\n// Define high level errors\n// Do *not* assign a Typescript type to the object\n// or IDE autocompletion will not work!\nconst errors = {\n  INTERNAL_SERVER_ERROR: {\n   /**\n    * The class name of the generated error\n    */\n    className: 'InternalServerError',\n    /**\n     * A user-friendly code to show to a client.\n     */\n    code: 'ERR_INT_500',\n   /**\n    * (optional) Protocol-specific status code, such as an HTTP status code. Used as the\n    * default if a Low Level Error status code is not defined.\n    */\n    statusCode: 500,\n    /**\n     * (optional) Log level string / number to associate with this error.\n     * Useful if you want to use your logging system to log the error but\n     * assign a different log level for it. Used as the default if a\n     * Low Level log level is not defined.\n     */\n    logLevel: 'error',\n    /**\n     * (optional) Callback function to call when calling BaseError#convert().\n     *\n     * (baseError) =\u003e any type\n     *\n     * - If not defined, will return itself when convert() is called\n     * - If defined in HighLevelError, the HighLevelError definition takes priority\n     */\n    onConvert: (err) =\u003e { return err },\n    /**\n     * (optional) Full description of the error. Used only when BaseError#newBareError() is called \n     * without the message parameter.\n     *\n     * sprintf() flags can be applied to customize it.\n     * @see https://www.npmjs.com/package/sprintf-js\n     */\n    message: 'Internal server error'\n  }\n}\n\n// Define low-level errors\n// Do *not* assign a Typescript type to the object\n// or IDE autocompletion will not work!\nconst errorSubCodes = {\n  // 'type' of error\n  DATABASE_FAILURE: {\n    /**\n     * Full description of the error. sprintf() flags can be applied\n     * to customize it.\n     * @see https://www.npmjs.com/package/sprintf-js\n     */\n    message: 'There was a database failure, SQL err code %s',\n    /**\n     * (optional) A user-friendly code to show to a client.\n     */\n    subCode: 'DB_0001',\n    /**\n     * (optional) Protocol-specific status code, such as an HTTP status code.\n     */\n    statusCode: 500,\n    /**\n     * (optional) Log level string / number to associate with this error.\n     * Useful if you want to use your logging system to log the error but\n     * assign a different log level for it.\n     */\n    logLevel: 'error',\n    /**\n     * (optional) Callback function to call when calling BaseError#convert().\n     *\n     * (baseError) =\u003e any type\n     *\n     * - If not defined, will return itself when convert() is called\n     * - This definition takes priority if HighLevelError#onConvert is defined\n     */\n    onConvert: (err) =\u003e { return err }\n  }\n}\n\n// Create the error registry by registering your errors and codes\n// you will want to memoize this as you will be using the\n// reference throughout your application\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes)\n\n// Create an instance of InternalServerError\n// Typescript autocomplete should show the available definitions as you type the error names\n// and type check will ensure that the values are valid\nconst err = errRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n  .withErrorId('err-1234')\n  .formatMessage('SQL_1234')\n\nconsole.log(err.toJSON())\n```\n\nProduces:\n\n(You can omit fields you do not need - see usage section below.)\n\n```\n{\n  errId: 'err-1234',\n  name: 'InternalServerError',\n  code: 'ERR_INT_500',\n  message: 'There was a database failure, SQL err code SQL_1234',\n  type: 'DATABASE_FAILURE',\n  subCode: 'DB_0001',\n  statusCode: 500,\n  meta: {},\n  stack: 'InternalServerError: There was a database failure, SQL err code %s\\n' +\n    '    at ErrorRegistry.newError (new-error/src/ErrorRegistry.ts:128:12)\\n' +\n    '    at Object.\u003canonymous\u003e (new-error/src/test.ts:55:25)\\n' +\n    '    at Module._compile (internal/modules/cjs/loader.js:1158:30)\\n' +\n    '    at Module._compile (new-error/node_modules/source-map-support/source-map-support.js:541:25)\\n' +\n    '    at Module.m._compile (/private/var/folders/mx/b54hc2lj3fbfsndkv4xmz8d80000gn/T/ts-node-dev-hook-20649714243977457.js:57:25)\\n' +\n    '    at Module._extensions..js (internal/modules/cjs/loader.js:1178:10)\\n' +\n    '    at require.extensions.\u003ccomputed\u003e (/private/var/folders/mx/b54hc2lj3fbfsndkv4xmz8d80000gn/T/ts-node-dev-hook-20649714243977457.js:59:14)\\n' +\n    '    at Object.nodeDevHook [as .ts] (new-error/node_modules/ts-node-dev/lib/hook.js:61:7)\\n' +\n    '    at Module.load (internal/modules/cjs/loader.js:1002:32)\\n' +\n    '    at Function.Module._load (internal/modules/cjs/loader.js:901:14)'\n}\n```\n\n### Helper utilities\n\n#### Auto-generate high level error properties\n\n`generateHighLevelErrors(errorDefs, options: GenerateHighLevelErrorOpts)`\n\nIf you find yourself doing the following pattern:\n\n```ts\nconst errors = {\n  INTERNAL_SERVER_ERROR: {\n    className: 'InternalServerError', // pascal case'd property name\n    code: 'INTERNAL_SERVER_ERROR', // same as the property name\n    statusCode: 500\n  }\n}\n```\n\nYou can use the utility method to do it instead:\n\n```ts\nimport { generateHighLevelErrors } from 'new-error'\n\nconst errors = generateHighLevelErrors({\n  INTERNAL_SERVER_ERROR: {\n    statusCode: 500\n  }\n})\n```\n\n- If a `className` or `code` is already defined, it will not overwrite it\n\n##### Configuration options\n\n```ts\ninterface GenerateHighLevelErrorOpts {\n  /**\n   * Disable to not generate the class name based on the property name if the className is not defined.\n   */\n  disableGenerateClassName?: boolean\n  /**\n   * Disable to not generate the error code based on the property name if the code is not defined.\n   */\n  disableGenerateCode?: boolean\n}\n```\n\n#### Auto-generate low level error properties\n\n`generateLowLevelErrors(errorDefs, options: GenerateLowLevelErrorOpts)`\n\nIf you find yourself doing the following pattern:\n\n```ts\nconst errors = {\n  DATABASE_FAILURE: {\n    subCode: 'DATABASE_FAILURE', // same as the property name\n    message: 'Database failure'\n  }\n}\n```\n\nYou can use the utility method to do it instead:\n\n```ts\nimport { generateLowLevelErrors } from 'new-error'\n\nconst errors = generateLowLevelErrors({\n  DATABASE_FAILURE: {\n    message: 'Database failure'\n  }\n})\n```\n\n- If a `subCode` is already defined, it will not overwrite it\n\n##### Configuration options\n\n```ts\ninterface GenerateLowLevelErrorOpts {\n  /**\n   * Disable to not generate the error code based on the property name if the subCode is not defined.\n   */\n  disableGenerateSubCode?: boolean\n}\n```\n\n## Class-based with low level errors without a registry\n\nYou can create concrete error classes by extending the `BaseRegistryError` class, which\nextends the `BaseError` class.\n\nThe registry example can be also written as:\n\n```typescript\nimport { BaseRegistryError, LowLevelError } from 'new-error'\n\nclass InternalServerError extends BaseRegistryError {\n  constructor (errDef: LowLevelError) {\n    super({\n      code: 'ERR_INT_500',\n      statusCode: 500\n    }, errDef)\n  }\n}\n\nconst err = new InternalServerError({\n  type: 'DATABASE_FAILURE',\n  message: 'There was a database failure, SQL err code %s',\n  subCode: 'DB_0001',\n  statusCode: 500,\n  logLevel: 'error'\n})\n\nconsole.log(err.formatMessage('SQL_1234').toJSON())\n```\n\n## Bare-bones class-based error\n\nIf you want a native-style `Error`, you can use `BaseError`.\n\nThe registry example can be written as:\n\n```typescript\nimport { BaseError } from 'new-error'\n\nclass InternalServerError extends BaseError {}\n\nconst err = new InternalServerError('There was a database failure, SQL err code %s')\n  // calling these methods are optional\n  .withErrorType('DATABASE_FAILURE')\n  .withErrorCode('ERR_INT_500')\n  .withErrorSubCode('DB_0001')\n  .withStatusCode(500)\n  .withLogLevel('error')\n\nconsole.log(err.formatMessage('SQL_1234').toJSON())\n```\n\n# Example Express Integration\n\n```typescript\nimport express from 'express'\nimport { ErrorRegistry, BaseError } from 'new-error'\nconst app = express()\nconst port = 3000\n\nconst errors = {\n  INTERNAL_SERVER_ERROR: {\n    className: 'InternalServerError',\n    code: 'ERR_INT_500',\n    statusCode: 500\n  }\n}\n\nconst errorSubCodes = {\n  DATABASE_FAILURE: {\n    message: 'There was a database failure.',\n    subCode: 'DB_0001',\n    statusCode: 500\n  }\n}\n\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes)\n\n// middleware definition\napp.get('/', async (req, res, next) =\u003e {\n  try {\n    // simulate a failure\n    throw new Error('SQL issue')\n  } catch (e) {\n    const err = errRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n    err.causedBy(err)\n    // errors must be passed to next()\n    // to be caught when using an async middleware\n    return next(err)\n  }\n})\n\n// catch errors\napp.use((err, req, res, next) =\u003e {\n  // error was sent from middleware\n  if (err) {\n    // check if the error is a generated one\n    if (err instanceof BaseError) {\n      // generate an error id\n      // you'll want to use a library like 'nanoid' instead\n      // this is just an example\n      err.withErrorId(Math.random().toString(36).slice(2))\n\n      // log the error\n      // the \"null, 2\" options formats the error into a readable structure\n      console.error(JSON.stringify(err.toJSON(), null, 2))\n\n      // get the status code, if the status code is not defined, default to 500\n      res.status(err.getStatusCode() ?? 500)\n      // spit out the error to the client\n      return res.json({\n        err: err.toJSONSafe()\n      })\n    }\n \n    // You'll need to modify code below to best fit your use-case\n    // err.message could potentially expose system internals\n    return res.json({\n      err: {\n        message: err.message\n      }\n    })\n  }\n\n  // no error, proceed\n  next()\n})\n\napp.listen(port, () =\u003e console.log(`Example app listening at http://localhost:${port}`))\n```\n\nIf you visit `http://localhost:3000`, you'll get a 500 status code, and the following response:\n\n```\n{\"err\": {\"errId\": \"xd0v1szkziq\", code\":\"ERR_INT_500\",\"subCode\":\"DB_0001\",\"statusCode\":500,\"meta\":{}}}\n```\n\n# Working with log levels\n\nYou might want to use a different log level when logging common errors, such as validation errors.\n\n```typescript\nimport { ErrorRegistry } from 'new-error'\n\nconst errors = {\n  VALIDATION_ERROR: {\n    className: 'ValidationError',\n    code: 'VALIDATION_ERROR',\n    statusCode: 400,\n    // probably don't want to log every validation error\n    // in production since these errors tend to happen frequently\n    // and would pollute the logs\n    logLevel: 'debug'\n  }\n}\n\nconst errorSubCodes = {\n  MISSING_FORM_FIELDS: {\n    message: 'Form submission data is missing fields',\n    subCode: 'MISSING_FORM_FIELDS',\n    statusCode: 400\n  }\n}\n\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes)\n\n// some part of the application throws the error\nconst err = errRegistry.newError('VALIDATION_ERROR', 'MISSING_FORM_FIELDS')\n\n// another part of the application catches the error\nif (err.getLogLevel() === 'debug') {\n  console.debug(JSON.stringify(err.toJSON(), null, 2))\n} else {\n  console.error(JSON.stringify(err.toJSON(), null, 2))\n}\n```\n\n# Error Registry API\n\nThe `ErrorRegistry` is responsible for the registration and creation of errors.\n\n## Constructor\n\n`new ErrorRegistry(highLvErrors, lowLvErrors, config = {})`\n\n### Configuration options\n\n```ts\ninterface IErrorRegistryConfig {\n  /**\n   * Options when creating a new BaseError\n   */\n  baseErrorConfig?: IBaseErrorConfig\n  /**\n   * Handler to modify the created error when newError / newBareError is called\n   */\n  onCreateError?: (err: BaseRegistryError) =\u003e void\n}\n```\n\n### Example\n\n```ts\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes, {\n  // Config for all BaseErrors created from the registry\n  baseErrorConfig: {\n    // Remove the `meta` field if there is no data present for `toJSON` / `toJSONSafe`\n    omitEmptyMetadata: true\n  }\n})\n```\n\n## Child registry with context\n\n`ErrorRegistry#withContext(context: IErrorRegistryContextConfig)`\n\nYou can create a child registry that adds context for all new errors created. This is useful if\nyour body of code throws multiple errors and you want to include the same metadata for each one\nwithout repeating yourself.\n\n- All property **references** are copied to the child registry from the parent. This keeps memory usage\nlow as the references are re-used vs a complete clone of the data.\n- Because all properties are copied over, the child registry will execute any handlers / config options\nthe parent has when creating new errors.\n\n### Configuration options\n\n```typescript\nexport interface IErrorRegistryContextConfig {\n  /**\n   * Metadata to include for each new error created by the registry\n   */\n  metadata?: Record\u003cany, string\u003e\n  /**\n   * Safe metadata to include for each new error created by the registry\n   */\n  safeMetadata?: Record\u003cany, string\u003e\n}\n```\n\n### Example\n\n```typescript\nconst childRegistry = errRegistry.withContext({\n  metadata: {\n    contextA: 'context-a'\n  },\n  safeMetadata: {\n    contextB: 'context-b'\n  }\n})\n\nconst err = childRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n```\n\nIf we do `err.toJSON()`, we should get the following output:\n\n```json5\n{\n  name: 'InternalServerError',\n  code: 'INT_ERR',\n  message: 'There is an issue with the database',\n  type: 'DATABASE_FAILURE',\n  subCode: 'DB_ERR',\n  statusCode: 500,\n  // err.toJSONSafe() would exclude contextA\n  meta: { contextA: 'context-a', contextB: 'context-b' },\n  stack: '...'\n}\n```\n\nWe can also append data:\n\n```typescript\nconst err = childRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n  .withMetadata({\n    moreMeta: 'data'\n  })\n```\n\nIf we do `err.toJSON()`, we should get the following output:\n\n```json5\n{\n  name: 'InternalServerError',\n  code: 'INT_ERR',\n  message: 'There is an issue with the database',\n  type: 'DATABASE_FAILURE',\n  subCode: 'DB_ERR',\n  statusCode: 500,\n  // err.toJSONSafe() would exclude contextA and moreMeta\n  meta: { contextA: 'context-a', contextB: 'context-b', moreMeta: 'data' },\n  stack: '...'\n}\n```\n\n## Creating errors\n\nErrors generated by the registry extends `BaseError`.\n\n### Create a well-defined error\n\nMethod: `ErrorRegistry#newError(highLevelErrorName, LowLevelErrorName)`\n\nThis is the method you should generally use as you are forced to use your\nwell-defined high and low level error definitions. This allows for consistency\nin how errors are defined and thrown.\n\n```typescript\n// Creates an InternalServerError error with a DATABASE_FAILURE code and corresponding\n// message and status code\nconst err = errRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n```\n\n### Create an error without a low-level error\n\nMethod: `ErrorRegistry#newBareError(highLevelErrorName, [message])`\n\nThis method does not include a low level error code, and allows direct specification of an\nerror message.\n\n#### Specify a custom message\n\n```typescript\n// Creates an InternalServerError error with a custom message\nconst err = errRegistry.newBareError('INTERNAL_SERVER_ERROR', 'An internal server error has occured.')\n```\n\n#### Use the message property from the high level error if defined\n\n```typescript\nconst errors = {\n  AUTH_REQUIRED: {\n    className: 'AuthRequired',\n    code: 'AUTH_REQ',\n    message: 'Auth required'\n  }\n}\n\n// Creates an AuthRequired error with a the 'Auth Required' message\nconst err = errRegistry.newBareError('AUTH_REQUIRED')\n```\n\n#### Custom message not defined and high level error has no message property defined\n\nThe error will use the code as the default.\n\n```typescript\nconst errors = {\n  DB_ERROR: {\n    className: 'DatabaseError',\n    code: 'DB_ERR'\n  }\n}\n\n// Creates an AuthRequired error with 'DB_ERR' as the message\nconst err = errRegistry.newBareError('DB_ERROR')\n```\n\n### Error creation handler\n\nIf you want all errors created from the registry to have defined properties, you can use the `onCreateError` config option to modify the created error.\n\nFor example, if you want to create an error id for each new error:\n\n```ts\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes, {\n  onCreateError: (err) =\u003e {\n    err.withErrorId('test-id')\n  }\n})\n\n// the err should have 'test-id' set for the error id\nconst err = errRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n```\n\n## `instanceOf` / comparisons\n\n### Comparing a custom error\n\nMethod: `ErrorRegistry#instanceOf(classInstance, highLevelErrorName)`\n\nPerforms an `instanceof` operation against a custom error.\n\n```typescript\n// creates an InternalServerError error instance\nconst err = errRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n\nif (errRegistry.instanceOf(err, 'INTERNAL_SERVER_ERROR')) {\n  // resolves to true since err is an InternalServerError instance\n}\n```\n\n### Native `instanceof`\n\nYou can also check if the error is custom-built using this check:\n\n```typescript\nimport { BaseError } from 'new-error'\n\nfunction handleError(err) {\n  if (err instanceof BaseError) {\n    // err is a custom error\n  }\n}\n```\n\n# Error API\n\nExcept for the getter and serialization methods, all other methods are chainable.\n\nGenerated errors extend the `BaseError` class, which extends `Error`.\n\n## Constructor\n\n`new BaseError(message: string, config: IBaseErrorConfig: IBaseErrorConfig = {})`\n\n- `message`: The error message you would use in `new Error(message)`\n\n### Configuration options\n\n```ts\ninterface IBaseErrorConfig {\n  /**\n   * A list of fields to always omit when calling toJSON\n   */\n  toJSONFieldsToOmit?: string[]\n  /**\n   * A list of fields to always omit when calling toJSONSafe\n   */\n  toJSONSafeFieldsToOmit?: string[]\n  /**\n   * If the metadata has no data defined, remove the `meta` property on `toJSON` / `toJSONSafe`.\n   */\n  omitEmptyMetadata?: boolean\n  /**\n   * A function to run against the computed data when calling `toJSON`. This is called prior\n   * to field omission. If defined, must return the data back.\n   */\n  onPreToJSONData?: (data: Partial\u003cSerializedError\u003e) =\u003e Partial\u003cSerializedError\u003e\n  /**\n   * A function to run against the computed safe data when calling `toJSONSafe`. This is called\n   * prior to field omission. If defined, must return the data back.\n   */\n  onPreToJSONSafeData?: (data: Partial\u003cSerializedErrorSafe\u003e) =\u003e Partial\u003cSerializedErrorSafe\u003e\n  /**\n   * A callback function to call when calling BaseError#convert(). This allows for user-defined conversion\n   * of the BaseError into some other type (such as a Apollo GraphQL error type).\n   *\n   * (baseError) =\u003e any type\n   */\n  onConvert?: \u003cE extends BaseError = BaseError\u003e(err: E) =\u003e any\n  /**\n   * If defined, will append the `.message` value when calling causedBy() after the main error message.\n   * Useful for frameworks like Jest where it will not print the caused by data.\n   * To define the format of the appended message, use '%s' for the message value.\n   *\n   * Ex: \", caused by: %s\"\n   */\n  appendWithErrorMessageFormat?: string\n}\n```\n\n## Getters\n\nThe following getters are included with the standard `Error` properties and methods:\n\n- `BaseError#getErrorId()`\n- `BaseError#getRequestId()`\n- `BaseError#getErrorName()`\n- `BaseError#getCode()`\n- `BaseError#getErrorType()`\n- `BaseError#getSubCode()`\n- `BaseError#getStatusCode()`\n- `BaseError#getCausedBy()`\n- `BaseError#getMetadata()`\n- `BaseError#getSafeMetadata()`\n- `BaseError#getLogLevel()`\n- `BaseError#getConfig()`\n\n## Basic setters\n\nIf you use the registry, you should not need to us these setters as the registry\nsets the values already.\n\n- `BaseError#withErrorType(type: string): this`\n- `BaseError#withErrorCode(code: string | number): this`\n- `BaseError#withErrorSubCode(code: string | number): this`\n- `BaseError#withLogLevel(level: string | number): this`\n- `BaseError#setConfig(config: IBaseErrorConfig): void`\n- `BaseError#setOnConvert(\u003cE extends BaseError = BaseError\u003e(err: E) =\u003e any): void`\n\n## Static methods\n\n- `static BaseError#fromJSON(data: object, options?: object): BaseError`\n\n## Utility methods\n\n- `BaseError#convert\u003cE = BaseError | any\u003e() : E`\n- `BaseError#hasOnConvertDefined(): boolean`\n\n## Set an error id\n\nMethod: `BaseError#withErrorId(errId: string)`\n\nAttaches an id to the error. Useful if you want to display an error id to a client / end-user\nand want to cross-reference that id in an internal logging system for easier troubleshooting.\n\nFor example, you might want to use [`nanoid`](https://github.com/ai/nanoid) to generate ids for errors.\n\n```typescript\nimport { nanoid } from 'nanoid'\n\nerr.withErrorId(nanoid())\n\n// In your logging system, log the error, which will include the error id\nlogger.error(err.toJSON())\n\n// expose the error to the client via err.toJSONSafe() or err.getErrorId(), which \n// will also include the error id - an end-user can reference this id to \n// support for troubleshooting\n```\n\n## Set a request id\n\nMethod: `BaseError#withRequestId(reqId: string)`\n\nAttaches request id to the error. Useful if you want to display the request id to a client / end-user\nand want to cross-reference that id in an internal logging system for easier troubleshooting.\n\nFor example, you might want to use [`nanoid`](https://github.com/ai/nanoid) to generate ids.\n\n```typescript\nimport { nanoid } from 'nanoid'\n\nerr.withRequestId(nanoid())\n\n// In your logging system, log the error, which will include the error id\nlogger.error(err.toJSON())\n\n// expose the error to the client via err.toJSONSafe() or err.getRequestId(), which \n// will also include the error id - an end-user can reference this id to \n// support for troubleshooting\n```\n\n## Attaching errors\n\nMethod: `BaseError#causedBy(err: any)`\n\nYou can attach another error to the error.\n\n```typescript\nconst externalError = new Error('Some thrown error')\nerr.causedBy(externalError)\n```\n\n### Append the attached error message to the main error message\n\nIf the config option `appendWithErrorMessageFormat` is defined, and the error sent into `causedBy`\ncontains a `message` property, then the caused by error message will be appended to the main error message.\n\nUseful if you find yourself applying this pattern to expose the attached error message:\n\n```typescript\nconst thrownErrorFromApp = new Error('Duplicate key error')\nconst err = new BaseError('Internal server error: %s');\nerr.causedBy(thrownErrorFromApp)\nerr.formatMessage(thrownErrorFromApp.message);\n```\n  \nThis is also useful for test frameworks like `jest` where it will only print out the main error message\nand not any properties attached to the error.\n\n```typescript\n// only enable for testing envs\nconst IS_TEST_ENV = process.env.NODE_ENV === 'test';\nconst err = new BaseError('Internal server error', {\n  // %s is the attached error message\n  appendWithErrorMessageFormat: IS_TEST_ENV ? ': %s' : null\n})\n\nerr.causedBy(new Error('Duplicate key'))\n\n// prints out \"Internal server error: Duplicate key\"\nconsole.log(err.message)\n```\n\n```typescript\n// formatted messages also work with this\nconst IS_TEST_ENV = process.env.NODE_ENV === 'test';\nconst err = new BaseError('Internal server error: %s', {\n  appendWithErrorMessageFormat: IS_TEST_ENV ? '===\u003e %s' : null\n})\n\n// formatMessage / causedBy can be called in any order\nerr.formatMessage('Hello')\nerr.causedBy(new Error('Duplicate key'))\n\n// prints out \"Internal server error: Hello ===\u003e Duplicate key\"\nconsole.log(err.message)\n```\n\n**It is not recommended that `appendWithErrorMessageFormat` is defined in a production environment\nas the `causedBy` error messages tend to be system-level messages that could be exposed to clients\nif the error is being thrown back to the client**.\n\n\n## Format messages\n\nMethod: `BaseError#formatMessage(...formatParams)`\n\nSee the [`sprintf-js`](https://www.npmjs.com/package/sprintf-js) package for usage.\n\n```typescript\n// specify the database specific error code\n// Transforms the message to:\n// 'There was a database failure, SQL err code %s' -\u003e\n// 'There was a database failure, SQL err code SQL_ERR_1234',\nerr.formatMessage('SQL_ERR_1234')\n```\n\nThe message can be accessed via the `.message` property.\n\n## Converting the error into another type\n\nMethod: `BaseError#convert\u003cT = any\u003e() : T`\n\nThis is useful if you need to convert the error into another type. This type can be another error or some other data type.\n\n### Apollo GraphQL example\n\nFor example, Apollo GraphQL prefers that any errors thrown from a GQL endpoint is an error that extends [`ApolloError`](https://www.apollographql.com/docs/apollo-server/data/errors/).\n\nYou might find yourself doing the following pattern if your resolver happens to throw a `BaseError`:\n\n```ts\nimport { GraphQLError } from 'graphql';\nimport { BaseError } from 'new-error';\nimport { ApolloError, ForbiddenError } from 'apollo-server';\n\nconst server = new ApolloServer({\n  typeDefs,\n  resolvers,\n  formatError: (err: GraphQLError) =\u003e {\n    const origError = error.originalError;\n\n    if (origError instanceof BaseError) {\n      // re-map the BaseError into an Apollo error type\n      switch(origError.getCode()) {\n        case 'PERMISSION_REQUIRED':\n          return new ForbiddenError(err.message)\n        default:\n          return new ApolloError(err.message)\n      }\n    }\n\n    return err;\n  },\n});\n```\n\nTrying to switch for every single code / subcode can be cumbersome.\n\nInstead of using this pattern, do the following so that your conversions can remain in one place:\n\n```ts\nimport { GraphQLError } from 'graphql';\nimport { BaseError, ErrorRegistry } from 'new-error';\nimport { ApolloError, ForbiddenError } from 'apollo-server';\n\nconst errors = {\n  PERMISSION_REQUIRED: {\n    className: 'PermissionRequiredError',\n    code: 'PERMISSION_REQUIRED',\n    // Define a conversion function that is called when BaseError#convert() is called\n    // error is the BaseError\n    onConvert: (error) =\u003e {\n      return new ForbiddenError(error.message)\n    }\n  },\n  AUTH_REQUIRED: {\n    className: 'AuthRequiredError',\n    code: 'AUTH_REQUIRED'\n  }\n}\n\nconst errorSubCodes = {\n  ADMIN_PANEL_RESTRICTED: {\n    message: 'Access scope required: admin',\n    onConvert: (error) =\u003e {\n      return new ForbiddenError('Admin required')\n    }\n  },\n  EDITOR_SECTION_RESTRICTED: {\n    message: 'Access scope required: editor',\n  }\n}\n\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes, {\n  onCreateError: (err) =\u003e {\n    // if an onConvert handler has not been defined, default to ApolloError\n    if (!err.hasOnConvertDefined()) {\n      err.setOnConvert((err) =\u003e {\n        if (process.env.NODE_ENV !== 'production') {\n          // return full error details\n          return new ApolloError(err.message, err.getCode(), err.toJSON());\n        }\n\n        // in production, we don't want to expose codes that are internal\n        return new ApolloError('Internal server error', 'INTERNAL_SERVER_ERROR', {\n          errId: err.getErrorId(),\n        });\n      });\n    }\n  }\n})\n\nconst server = new ApolloServer({\n  typeDefs,\n  resolvers,\n  // errors thrown from the resolvers come here\n  formatError: (err: GraphQLError) =\u003e {\n    const origError = error.originalError;\n\n    if (origError instanceof BaseError) {\n      // log out the original error\n      console.log(origError.toJSON())\n\n      // Convert out to an Apollo error type\n      return origError.convert()\n    }\n    \n    // log out the apollo error\n    // you would probably want to see if you can convert this\n    // to a BaseError type in your code where this may be thrown from\n    console.log(err)\n    \n    return err;\n  },\n});\n\nconst resolvers = {\n  Query: {\n    adminSettings(parent, args, context, info) {\n      // err.convert() will call onConvert() of ADMIN_PANEL_RESTRICTED (low level defs have higher priority)\n      throw errRegistry.newError('PERMISSION_REQUIRED', 'ADMIN_PANEL_RESTRICTED')\n    },\n    editorSettings(parent, args, context, info) {\n      // err.convert() will call onConvert() of PERMISSION_REQUIRED since EDITOR_SECTION_RESTRICTED does not\n      // have the onConvert defined\n      throw errRegistry.newError('PERMISSION_REQUIRED', 'EDITOR_SECTION_RESTRICTED')\n    },\n    checkAuth(parent, args, context, info) {\n      // err.convert() will return ApolloError from onCreateError() since onConvert() is not defined for either AUTH_REQUIRED or EDITOR_SECTION_RESTRICTED\n      throw errRegistry.newError('AUTH_REQUIRED', 'EDITOR_SECTION_RESTRICTED')\n    },\n    checkAuth2(parent, args, context, info) {\n      // err.convert() will return ApolloError from onCreateError() since onConvert() is not defined for either AUTH_REQUIRED\n      throw errRegistry.newBareError('AUTH_REQUIRED', 'Some error message')\n    },\n    permRequired(parent, args, context, info) {\n      // err.convert() will call onConvert() of PERMISSION_REQUIRED\n      throw errRegistry.newBareError('PERMISSION_REQUIRED', 'Some error message')\n    }\n  }\n}\n```\n\n## Adding metadata\n\n### Safe metadata\n\nMethod: `BaseError#withSafeMetadata(data = {})`\n\nSafe metadata would be any kind of data that you would be ok with exposing to a client, like an\nHTTP response.\n\n```typescript\nerr.withSafeMetadata({\n  errorId: 'err-12345',\n  moreData: 1234\n})\n// can be chained to append more data\n.withSafeMetadata({\n  requestId: 'req-12345'\n})\n```\n\nThis can also be written as:\n\n```typescript\nerr.withSafeMetadata({\n  errorId: 'err-12345',\n  moreData: 1234\n})\n\n// This will append requestId to the metadata\nerr.withSafeMetadata({\n  requestId: 'req-12345'\n})\n```\n\n### Internal metadata\n\nMethod: `BaseError#withMetadata(data = {})`\n\nInternal metadata would be any kind of data that you would *not be* ok with exposing to a client,\nbut would be useful for internal development / logging purposes.\n\n```typescript\nerr.withMetadata({\n  email: 'test@test.com'\n})\n// can be chained to append more data\n.withMetadata({\n  userId: 'user-abcd'\n})\n```\n\n## Serializing errors\n\n### Safe serialization\n\nMethod: `BaseError#toJSONSafe(fieldsToOmit = [])`\n\nGenerates output that would be safe for client consumption.\n\n- Omits `name`\n- Omits `message`\n- Omits `causedBy`\n- Omits `type`\n- Omits `logLevel`\n- Omits the stack trace\n- Omits any data defined via `BaseError#withMetadata()`\n\n```typescript\nerr.withSafeMetadata({\n  requestId: 'req-12345'\n})\n// you can remove additional fields by specifying property names in an array\n//.toJSONSafe(['code']) removes the code field from output\n.toJSONSafe()\n```\n\nProduces:\n\n```\n{\n  code: 'ERR_INT_500',\n  subCode: 'DB_0001',\n  statusCode: 500,\n  meta: { requestId: 'req-12345' }\n}\n```\n\n### Internal serialization\n\nMethod: `BaseError#toJSON(fieldsToOmit = [])`\n\nGenerates output that would be suitable for internal use.\n\n- Includes `name`\n- Includes `type`\n- Includes `message`\n- Includes `causedBy`\n- Includes the stack trace\n- All data from `BaseError#withMetadata()` and `BaseError#withSafeMetadata()` is included\n\n```typescript\nerr.withSafeMetadata({\n  reqId: 'req-12345',\n}).withMetadata({\n  email: 'test@test.com'\n})\n// you can remove additional fields by specifying property names in an array\n//.toJSON(['code', 'statusCode']) removes the code and statusCode field from output\n.toJSON()\n```\n\nProduces:\n\n```\n{\n  name: 'InternalServerError',\n  code: 'ERR_INT_500',\n  message: 'There was a database failure, SQL err code %s',\n  type: 'DATABASE_FAILURE',\n  subCode: 'DB_0001',\n  statusCode: 500,\n  meta: { errorId: 'err-12345', requestId: 'req-12345' },\n  stack: 'InternalServerError: There was a database failure, SQL err code %s\\n' +\n    '    at ErrorRegistry.newError (new-error/src/ErrorRegistry.ts:128:12)\\n' +\n    '    at Object.\u003canonymous\u003e (new-error/src/test.ts:55:25)\\n' +\n    '    at Module._compile (internal/modules/cjs/loader.js:1158:30)\\n' +\n    '    at Module._compile (new-error/node_modules/source-map-support/source-map-support.js:541:25)\\n' +\n    '    at Module.m._compile (/private/var/folders/mx/b54hc2lj3fbfsndkv4xmz8d80000gn/T/ts-node-dev-hook-17091160954051898.js:57:25)\\n' +\n    '    at Module._extensions..js (internal/modules/cjs/loader.js:1178:10)\\n' +\n    '    at require.extensions.\u003ccomputed\u003e (/private/var/folders/mx/b54hc2lj3fbfsndkv4xmz8d80000gn/T/ts-node-dev-hook-17091160954051898.js:59:14)\\n' +\n    '    at Object.nodeDevHook [as .ts] (new-error/node_modules/ts-node-dev/lib/hook.js:61:7)\\n' +\n    '    at Module.load (internal/modules/cjs/loader.js:1002:32)\\n' +\n    '    at Function.Module._load (internal/modules/cjs/loader.js:901:14)'\n}\n```\n\n### Post-processing handlers\n\nThe `BaseError` config `onPreToJSONData` / `onPreToJSONSafeData` options allow post-processing of the data. This is useful if you want to decorate your data for all new\nerrors created.\n\n```ts\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes, {\n  baseErrorConfig: {\n    // called when toJSON is called\n    onPreToJSONData: (data) =\u003e {\n      // we want all new errors to contain a date field\n      data.date = new Date().tostring()\n\n      // add some additional metadata\n      // data.meta might be empty if omitEmptyMetadata is enabled\n      if (data.meta) {\n        data.meta.moreData = 'test'\n      }\n\n      return data\n    }    \n  }\n})\n\nconst err = errRegistry.newError('INTERNAL_SERVER_ERROR', 'DATABASE_FAILURE')\n  .withErrorId('err-1234')\n  .formatMessage('SQL_1234')\n\n// should produce the standard error structure, but with the new fields added\nconsole.log(err.toJSON())\n```\n\n# Deserialization\n\n## Issues with deserialization\n\n### Deserialization is not perfect\n\n- If the serialized output lacks the `name` property (not present when using `toJSONSafe()`), then only a `BaseError` instance can be returned.\n- The metadata is squashed in the serialized output that information is required to separate them.\n- It is difficult to determine the original type / structure of the `causedBy` data. As a result, it will be copied as-is.\n\n### Potential security issues with deserialization\n\n- You need to be able to trust the data you're deserializing as the serialized data can be modified in various ways by\nan untrusted party.\n- The deserialization implementation does not perform `JSON.parse()` as `JSON.parse()` in its raw form is susceptible to\n[prototype pollution](https://medium.com/intrinsic/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96)\n if the parse function does not have a proper sanitization function. It is up to the developer to properly \n trust / sanitize / parse the data.\n \n## `ErrorRegistry#fromJSON()` method\n\nThis method will attempt to deserialize into a registered error type via the `name` property. If it is unable to, a `BaseError` instance is\nreturned instead.\n\n`ErrorRegistry#fromJSON(data: object, [options]: DeserializeOpts): IBaseError`\n\n- `data`: Data that is the output of `BaseError#toJSON()`. The data must be an object, not a string.\n- `options`: Optional deserialization options.\n\n```typescript\ninterface DeserializeOpts {\n  /**\n   * Fields from meta to pluck as a safe metadata field\n   */\n  safeMetadataFields?: {\n    // the value must be set to true.\n    [key: string]: true\n  }\n}\n```\n\nReturns a `BaseError` instance or an instance of a registered error type.\n\n```typescript\nimport { ErrorRegistry } from 'new-error'\n\nconst errors = {\n  INTERNAL_SERVER_ERROR: {\n    className: 'InternalServerError',\n    code: 'ERR_INT_500',\n    statusCode: 500,\n    logLevel: 'error'\n  }\n}\n\nconst errorSubCodes = {\n  DATABASE_FAILURE: {\n    message: 'There was a database failure, SQL err code %s',\n    subCode: 'DB_0001',\n    statusCode: 500,\n    logLevel: 'error'\n  }\n}\n\nconst errRegistry = new ErrorRegistry(errors, errorSubCodes)\n\nconst data = {\n    'errId': 'err-123',\n    'code': 'ERR_INT_500',\n    'subCode': 'DB_0001',\n    'message': 'test message',\n    'meta': { 'safeData': 'test454', 'test': 'test123' },\n    // maps to className in the high level error def\n    'name': 'InternalServerError',\n    'statusCode': 500,\n    'causedBy': 'test',\n    'stack': 'abcd'\n}\n\n// err should be an instance of InternalServerError\nconst err = errRegistry.fromJSON(data, {\n  safeMetadataFields: {\n    safeData: true\n  }\n})\n```\n\n## `static BaseError#fromJSON()` method\n\nIf you are not using the registry, you can deserialize using this method. This also applies to any class that extends\n`BaseError`.\n\n`static BaseError#fromJSON(data: object, [options]: DeserializeOpts): IBaseError`\n\n- `data`: Data that is the output of `BaseError#toJSON()`. The data must be an object, not a string.\n- `options`: Optional deserialization options.\n\nReturns a `BaseError` instance or an instance of the class that extends it.\n\n```typescript\nimport { BaseError } from 'new-error'\n\n// assume we have serialized error data\nconst data = {\n  code: 'ERR_INT_500',\n  subCode: 'DB_0001',\n  statusCode: 500,\n  errId: 'err-1234',\n  meta: { requestId: 'req-12345', safeData: '123' }\n}\n\n// deserialize\n// specify meta field assignment - fields that are not assigned will be assumed as withMetadata() type data\nconst err = BaseError.fromJSON(data, {\n  // (optional) Fields to pluck from 'meta' to be sent to BaseError#safeMetadataFields()\n  // value must be set to 'true'\n  safeMetadataFields: {\n    safeData: true\n  }\n})\n```\n\n## Stand-alone instance-based deserialization\n\nIf the `name` property is present in the serialized data if it was serialized with `toJson()`, you can use a switch \nto map to an instance:\n\n```typescript\nconst data = {\n  // be sure that you trust the source of the deserialized data!\n  // anyone can modify the 'name' property to whatever\n  name: 'InternalServerError',\n  code: 'ERR_INT_500',\n  subCode: 'DB_0001',\n  statusCode: 500,\n  errId: 'err-1234',\n  meta: { requestId: 'req-12345', safeData: '123' }\n}\n\nlet err = null\n\nswitch (data.name) {\n  case 'InternalServerError':\n    // assume InternalServerError extends BaseError\n    return InternalServerError.fromJSON(data)\n  default:\n    return BaseError.fromJSON(data)\n}\n```\n\n# Looking for production-grade env variable / configuration management?\n\nCheck out https://github.com/theogravity/configurity\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheogravity%2Fnew-error","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheogravity%2Fnew-error","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheogravity%2Fnew-error/lists"}