UNPKG

zod-error

Version:

Utilities to format and customize Zod error messages

401 lines (311 loc) • 19.8 kB
<h1 align="center">Zod Error</h1> <div align="center"> [![Status](https://img.shields.io/badge/status-active-blue)](https://github.com/andrewvo89/zod-error) [![GitHub Issues](https://img.shields.io/github/issues/andrewvo89/zod-error?color=blue)](https://github.com/andrewvo89/zod-error/issues) [![GitHub Pull Requests](https://img.shields.io/github/issues-pr/andrewvo89/zod-error?color=blue)](https://github.com/andrewvo89/zod-error/pulls) [![License](https://img.shields.io/github/license/andrewvo89/zod-error?color=blue)](/LICENSE) </div> --- <p align="center">Utilities to format and customize Zod error messages.</p> ## Table of Contents - [About](#about) - [Installation](#installation) - [Usage](#usage) - [Authors](#authors) - [Acknowledgments](#acknowledgements) ## About Zod Error converts and formats Zod Issues into a customizable error message string that can be consumed by various applications such as browser error message modals or server api error messages. [Zod v4](https://zod.dev/v4) has a simple API to stringify errors. It may be sufficient enough for your needs: https://zod.dev/error-formatting?id=zprettifyerror ### Basic Usage Zod Error converts an array of Zod Issues that look like this: ```ts [ { code: 'invalid_type', expected: 'string', received: 'undefined', path: ['name'], message: 'Required', }, { code: 'invalid_type', expected: 'string', received: 'number', path: ['pets', 1], message: 'Expected string, received number', }, ]; ``` into this: ``` Error #1: Code: invalid_type ~ Path: name ~ Message: Required | Error #2: Code: invalid_type ~ Path: pets[1] ~ Message: Expected string, received number ``` ## Versions With the release of [Zod v4](https://zod.dev/v4), `zod-error` has moved to v2 to meet the new API. | Zod | Zod Error | |-----|-----------| | 3.x.x | 1.x.x | | 4.x.x | 2.x.x | ## Installation Install the package using your favorite package manager: ``` npm install zod-error yarn add zod-error pnpm add zod-error ``` ## Usage ### Message Format ``` šŸ•“ 2022-07-14T20:19:52.290Z ~ Error #1: Code: invalid_type ~ Path: ratings[0].speed ~ Message: Expected number, received string šŸ”„ Error #2: Code: invalid_enum_value ~ Path: position ~ Message: Invalid enum value. Expected 'C' | 'PF' | 'SF' | 'SG' | 'PG', received 'Center'šŸ”š ``` | Value | Description | | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | | `šŸ•“ 2022-07-14T20:19:15.660Z ~` | Prefix | | `~` | Component delimiter | | `Error #1:` | Added using `options.transform()` | | `Code: ` | Code label | | `invalid_type` | Code value | | `Path: ` | Path label | | `ratings[0].speed` | Path value | | `Message: ` | Message label | | `Expected number, received string` | Message value | | `šŸ”„` | Error delimiter | | `Error #2: Code: invalid_enum_value ~ Path: position ~ Message: Invalid enum value. Expected 'C' \| 'PF' \| 'SF' \| 'SG'\| 'PG', received 'Center'` | Error from second ZodIssue from Issues array input | | `šŸ”š` | Suffix | ### Options Error messages are completely customizable from label names to delimiters, prefixes, suffixes and the inclusion/exclusion of components (code, path, message). An options argument can be passed to any Zod Error function as the last argument to customize the error message. | Property | Value | Description | | ---------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------- | | code? | [CodeOptions](#codeoptions) | Options to customize the code component of the error message. | | delimiter? | [DelimiterOptions](#delimiteroptions) | Set the delimiter between error messages and between components. | | maxErrors? | number | Maximum amount of error messages to display in final concatenated string. | | message? | [MessageOptions](#messageoptions) | Options to customize the message component of the error message. | | path? | [PathOptions](#pathoptions) | Options to customize the code path of the error message. | | prefix? | string | Add a prefix to the start of the final concatenated message. | | suffix? | string | Add a suffix to the end of the final concatenated string. | | transform? | (params: [TransformErrorParams](#transformerrorparams)) => string | A custom function to transform the format of each error message. | ### CodeOptions | Property | Value | Description | | ---------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | enabled | boolean | Display or hide the code component of the error message. Defaults to `true`. | | label? | string \| null | Set a custom label. Defaults to `Code: `. Only available if `enabled` is `true`. | | transform? | (params: [TransformComponentParams](#transformcomponentparams)) => string | A custom function to transform the format of the code component. Only available if `enabled` is `true`. | ### DelimiterOptions | Property | Value | Description | | ---------- | ------ | --------------------------------------------------------------------------------------------- | | component? | string | The delimiter between each component during the concatentation process. Defaults to `~`. | | error? | string | The delimiter between each error message during the concatentation process. Defaults to `\|`. | ### MessageOptions | Property | Value | Description | | ---------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | enabled | boolean | Display or hide the message component of the error message. Defaults to `true`. | | label? | string \| null | Set a custom label. Defaults to `Message: `. Only available if `enabled` is `true`. | | transform? | (params: [TransformComponentParams](#transformcomponentparams)) => string | A custom function to transform the format of the message component. Only available if `enabled` is `true`. | ### PathOptions | Property | Value | Description | | -------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | arraySquareBrackets? | boolean | Adds square brackets around index number in the path. Only available if `enabled` is `true` and `type` is `objectNotation` or `breadcrumbs`. Defaults to `true`. | | delimiter? | string | Set a custom delimeter between each path element. Only available if `enabled` is `true` and `type` is `breadcrumbs`. Defaults to `>`. | | enabled | boolean | Display or hide the path component of the error message. Defaults to `true`. | | label? | string \| null | Set a custom label. Defaults to `Message: `. Only available if `enabled` is `true`. | | transform? | (params: [TransformComponentParams](#transformcomponentparams)) => string | A custom function to transform the format of the message component. Only available if `enabled` is `true`. | | type | 'objectNotation' \| 'zodPathArray' \| 'breadcrumbs' | Sets the style of the path string.<br/>objectNotation = car.wheels[1].tyre <br/>zodPathArray = ["car", "wheels", 1, "tyre"]<br/>breadcrumbs = car > wheels > [1] > tyre. | ### TransformComponentParams | Property | Value | Description | | --------- | ------ | ----------------------------------------------------------------- | | component | string | The transformed component string. Defaults to `${label}${value}`. | | label | string | The label of the component. | | value | string | The value of the component. | ### TransformErrorParams | Property | Value | Description | | ---------------- | ---------------- | ------------------------------------------------------------------------- | | codeComponent | string | The transformed code component string. Defaults to `${label}${value}`. | | errorMessage | string | The transformed error message consisting of all components concatentated. | | index | string | The index of the current error message. | | issue | z.core.$ZodIssue | The original ZodIssue object. | | messageComponent | string | The transformed message component string. Defaults to `${label}${value}`. | | pathComponent | string | The transformed path component string. Defaults to `${label}${value}`. | ### Examples There are 6 ways to consume Zod Error. `generateErrorMessage()`, `generateError()`, `parse()`, `parseAsync()`, `safeParse()` and `safeParseAsync()`. #### `generateErrorMessage(issues: z.core.$ZodIssue[], options?: ErrorMessageOptions): string` Formats an array of Zod Issues as a result of `z.parse()`, `z.parseAsync()`, `z.safeParse()` or `z.safeParseAsync()` and outputs as a single string. Multiple errors are concatenated into a single readable string. ```ts import { generateErrorMessage, ErrorMessageOptions } from 'zod-error'; import { z } from 'zod'; enum Color { Red = 'Red', Blue = 'Blue', } const options: ErrorMessageOptions = { delimiter: { error: ' šŸ”„ ', }, transform: ({ errorMessage, index }) => `Error #${index + 1}: ${errorMessage}`, }; const schema = z.object({ color: z.enum(Color), shape: z.string(), size: z.number().gt(0), }); const data = { color: 'Green', size: -1, }; const result = schema.safeParse(data); if (!result.success) { const errorMessage = generateErrorMessage(result.error.issues, options); throw new Error(errorMessage); } ``` Error Message: ``` Error #1: Code: invalid_enum_value ~ Path: color ~ Message: Invalid enum value. Expected 'Red' | 'Blue', received 'Green' šŸ”„ Error #2: Code: invalid_type ~ Path: shape ~ Message: Required šŸ”„ Error #3: Code: too_small ~ Path: size ~ Message: Number must be greater than 0 ``` #### `generateError(issues: z.core.$ZodIssue[], options?: ErrorMessageOptions): Error` Formats an array of Zod Issues as a result of `z.parse()`, `z.parseAsync()`, `z.safeParse()` or `z.safeParseAsync()` and outputs as a JavaScript Error object. Multiple errors are concatenated into a single readable string. ```ts import { ErrorMessageOptions, generateError } from 'zod-error'; import { z } from 'zod'; const options: ErrorMessageOptions = { maxErrors: 2, delimiter: { component: ' - ', }, path: { enabled: true, type: 'zodPathArray', label: 'Zod Path: ', }, code: { enabled: false, }, message: { enabled: true, label: '', }, }; const schema = z.object({ dates: z.object({ purchased: z.date(), fulfilled: z.date(), }), item: z.string(), price: z.number(), }); const data = { dates: { purchased: 'yesterday' }, item: 1, price: '1,000', }; try { schema.parse(data); } catch (error) { const genericError = generateError(error, options); throw genericError; } ``` Error Message: ``` Zod Path: ["dates", "purchased"] - Expected date, received string | Zod Path: ["dates", "fulfilled"] - Required ``` #### `parse<T extends z.ZodTypeAny>(schema: T, data: unknown, options?: ErrorMessageOptions): T['_output']` Replaces Zod's `.parse()` function by replacing Zod's `ZodError` with a generic JavaScript `Error` object where the custom formatted message can be accessed on `error.message`. ```ts import { ErrorMessageOptions, parse } from 'zod-error'; import { z } from 'zod'; const options: ErrorMessageOptions = { delimiter: { error: ' ', }, path: { enabled: true, type: 'objectNotation', transform: ({ label, value }) => `<${label}: ${value}>`, }, code: { enabled: true, transform: ({ label, value }) => `<${label}: ${value}>`, }, message: { enabled: true, transform: ({ label, value }) => `<${label}: ${value}>`, }, transform: ({ errorMessage }) => `šŸ‘‰ ${errorMessage} šŸ‘ˆ`, }; const schema = z.object({ animal: z.enum(['🐶', '🐱', '🐵']), quantity: z.number().gte(1), }); const data = { animal: '🐼', quantity: 0, }; try { const safeData = parse(schema, data, options); /** * Asynchronous version * const safeData = await parseAsync(schema, data, options); */ } catch (error) { /** * Replaces ZodError with a JavaScript * Error object with custom formatted message. */ if (error instanceof Error) { console.error(error.message); } } ``` Error Message: ``` šŸ‘‰ <Code: : invalid_enum_value> ~ <Path: : animal> ~ <Message: : Invalid enum value. Expected '🐶' | '🐱' | '🐵', received '🐼'> šŸ‘ˆ šŸ‘‰ <Code: : too_small> ~ <Path: : quantity> ~ <Message: : Number must be greater than or equal to 1> šŸ‘ˆ ``` Note: > If your schema contains an async `.refine()` or `.transform()` function, use `parseAsync()` instead. #### `safeParse<T extends z.ZodTypeAny>(schema: T, data: unknown, options?: ErrorMessageOptions): SafeParseReturnType<T['_output']` Replaces Zod's `.safeParse()` function by replacing Zod's `SafeParseReturnType` with a similar return type where if `result.success` is `false`, the custom formatted error message will be available on `result.error.message`. ```ts import { ErrorMessageOptions, safeParse } from 'zod-error'; import { z } from 'zod'; const options: ErrorMessageOptions = { prefix: `Time: ${new Date().toISOString()} ~ `, suffix: 'šŸ”š', }; const schema = z.object({ id: z.uuid(), timestamp: z.number(), message: z.string().min(5), }); const data = { id: 'ID001', timestamp: new Date(), message: 'lol!', }; const result = safeParse(schema, data, options); /** * Asynchronous version * const result = await safeParseAsync(schema, data, options); */ if (!result.success) { /** * Replaces Zod's error object with custom * error object with formatted message. */ const message = result.error.message; console.error(message); } else { const safeData = result.data; } ``` Error Message: ``` Time: 2022-07-14T11:10:10.602Z ~ Code: invalid_string ~ Path: id ~ Message: Invalid uuid | Code: invalid_type ~ Path: timestamp ~ Message: Expected number, received date | Code: too_small ~ Path: message ~ Message: String must contain at least 5 character(s)šŸ”š ``` Note: > If your schema contains an async `.refine()` or `.transform()` function, use `safeParseAsync()` instead. ## Authors - [@andrewvo89](https://github.com/andrewvo89) - Idea & Initial work. See also the list of [contributors](https://github.com/andrewvo89/zod-error/contributors) who participated in this project. ## Acknowledgements - [Zod](https://zod.dev/) for an amazing validation library.