Skip to content

Commit c9ae089

Browse files
committedFeb 5, 2024
docs(website): add content to custom validation errors section
1 parent 0dadf3e commit c9ae089

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed
 

‎website/docs/usage/action-result-object.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ Here's how action result object is structured (all keys are optional):
1111
| Name | When | Value |
1212
|--------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
1313
| `data?` | Execution is successful. | What you returned in action's server code. |
14-
| `validationErrors?` | Input data doesn't pass validation. | An object whose keys are the names of the fields that failed validation. Each key's value is either an `ErrorList` or a nested key with an `ErrorList` inside.<br />`ErrorList` is defined as: `{ errors?: string[] }`.
14+
| `validationErrors?` | Input data doesn't pass validation. | An object whose keys are the names of the fields that failed validation. Each key's value is either an `ErrorList` or a nested key with an `ErrorList` inside.<br />`ErrorList` is defined as: `{ errors?: string[] }`.<br />It follows the same structure as [Zod's `format` function](https://zod.dev/ERROR_HANDLING?id=formatting-errors).
1515
| `serverError?` | An error occurs during action's server code execution. | A `string` that by default is "Something went wrong while executing the operation" for every server error that occurs, but this is [configurable](/docs/safe-action-client/custom-server-error-handling#handlereturnedservererror) when instantiating a new client. |

‎website/docs/usage/custom-validation-errors.md

+53-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,56 @@ description: Set custom validation errors in schema or in action's server code f
55

66
# Custom validation errors
77

8-
TODO: WIP
8+
When input data fails schema validation, a `validationErrors` object is returned to the client. This object contains all the fields that failed validation, and their corresponding error messages.
9+
10+
It's often useful to also define custom logic to set additional validation errors by ourselves, for example when a user is signing up and password/confirm password fields don't match, and/or when the email is already in use.
11+
12+
Let's see how to implement this specific case in the optimal way, using both schema refinements and errors set in action's server code function, thanks to `returnValidationErrors`.
13+
14+
## Schema refinements
15+
16+
First of all, we must check if the password and confirm password fields match. Using Zod in this example as our validation library, we can utilize `.refine` or `.superRefine` at the schema level to do that:
17+
18+
```typescript
19+
import { z } from "zod";
20+
21+
const schema = z.object({
22+
email: z.string().email(),
23+
password: z.string().min(8).max(100),
24+
confirmPassword: z.string().min(8).max(100),
25+
}).refine(({ password, confirmPassword }) => password === confirmPassword, {
26+
path: ["confirmPassword"],
27+
message: "Passwords do not match",
28+
});
29+
```
30+
31+
If the two fields don't match, a custom validation error will be set for the `confirmPassword` field. This is the perfect place to make this check, because verifying that two fields are the same should be a schema job.
32+
33+
## `returnValidationErrors`
34+
35+
When registering a new user, we also need to check if the email is already stored in the database, and if so, inform the user that that address is taken by someone else. The best place to make this check is inside the action's server code function. If we find out that the email is already taken by another user, we can return a custom validation error to the client using `returnValidationErrors`:
36+
37+
```typescript
38+
import { returnValidationErrors } from "next-safe-action";
39+
import { action } from "@/lib/safe-action";
40+
41+
// Here we're using the same schema declared above.
42+
const signupAction = action(schema, async ({email}) => {
43+
// Assume this is a database call.
44+
if (!isEmailAvailable(email)) {
45+
returnValidationErrors(schema, {
46+
email: {
47+
_errors: ["Email already registered"],
48+
},
49+
});
50+
}
51+
52+
...
53+
});
54+
```
55+
56+
Note that:
57+
- You're required to pass a schema as the first argument of `returnValidationErrors`. This is used to infer the type of the validation errors to set via the second argument.
58+
- Errors set using `returnValidationErrors` will not be merged with the schema ones. If schema validation fails, the execution stops before reaching action's server code function. Otherwise, the action's backend code would receive invalid parsed input.
59+
- `returnValidationErrors` returns `never`. This means that internally it throws an error that gets caught and processed by next-safe-action, so code declared below the `returnValidationErrors` invocation will not be executed.
60+
- Since it returns `never`, you don't need to use `return` before this function call, and you can call it only once per execution path (it works the same way as Next.js `redirect` and `notFound` functions).

0 commit comments

Comments
 (0)
Please sign in to comment.