zod-error
v2.0.0
Published
Utilities to format and customize Zod error messages
Maintainers
Readme
Table of Contents
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 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:
[
{
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 numberVersions
With the release of Zod 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-errorUsage
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 | Options to customize the code component of the error message. | | delimiter? | 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 | Options to customize the message component of the error message. | | path? | 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) => 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) => 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) => 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) => 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.objectNotation = car.wheels[1].tyre zodPathArray = ["car", "wheels", 1, "tyre"]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.
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 0generateError(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.
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"] - Requiredparse<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.
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, useparseAsync()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.
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, usesafeParseAsync()instead.
Authors
- @andrewvo89 - Idea & Initial work.
See also the list of contributors who participated in this project.
Acknowledgements
- Zod for an amazing validation library.
