Skip to content

Commit

Permalink
chore(website): improve documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
TheEdoRan committed Aug 29, 2024
1 parent 7f9888a commit a46b71a
Show file tree
Hide file tree
Showing 55 changed files with 1,362 additions and 1,741 deletions.
4 changes: 4 additions & 0 deletions website/docs/define-actions/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"label": "Define actions",
"position": 2
}
13 changes: 13 additions & 0 deletions website/docs/define-actions/action-result-object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
sidebar_position: 8
description: Action result object is the result of an action execution.
---

# Action result object

Here's how action result object is structured (all keys are optional):

- `data`: when execution is successful, what you returned in action's server code.
- `validationErrors`: when input data doesn't pass validation, an object that contains the validation errors. Can be customized using [`defaultValidationErrorsShape`](/docs/define-actions/create-the-client#defaultvalidationerrorsshape) initialization option and/or via [`handleValidationErrorsShape`function passed to `schema` method](/docs/define-actions/validation-errors#customize-validation-errors-format).
- `bindArgsValidationErrors`: when bound arguments don't pass validation, an object that contains the validation errors. Can be customized using [`defaultValidationErrorsShape`](/docs/define-actions/create-the-client#defaultvalidationerrorsshape) initialization option and/or via [`handleBindArgsValidationErrorsShape` function passed to `bindArgsSchemas` method](/docs/define-actions/validation-errors#customize-validation-errors-format).
- `serverError`: when execution fails, an error object that contains the error message, customizable by using the [`handleReturnedServerError`](/docs/define-actions/create-the-client#handlereturnedservererror) initialization function.
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
---
sidebar_position: 3
sidebar_position: 7
description: Action utils is an object with useful properties and callbacks functions that you can use to customize the action execution flow.
---

# Action utils

Action utils is an object with some useful properties and callbacks passed as the second argument of the [`action`/`stateAction`](/docs/safe-action-client/instance-methods#action--stateaction) method.
Action utils is an object with some useful properties and callbacks passed as the second argument of the [`action`/`stateAction`](/docs/define-actions/instance-methods#action--stateaction) method.

## Throw errors when they occur

Starting from v7.4.0, you can now pass optional `throwServerError` and `throwValidationErrors` properties at the action level, if you want or need that behavior. Note that the `throwValidationErrors` property set at the action level has a higher priority than the one at the instance level, so if you set it to `false` while the one at the instance level is `true`, validation errors will **not** be thrown.


## Callbacks
## Action callbacks

With action callbacks you can perform custom logic after the action is executed, on the server side. You can provide them to [`action`/`stateAction`](/docs/safe-action-client/instance-methods#action--stateaction) method in the second argument, after the server code function:
- `onSuccess`: called when action execution succeeds.
- `onError`: called when action execution fails (validation errors or server error).
- `onSettled`: called when action execution succeeds or fails.

With action callbacks you can perform custom logic after the action is executed, on the server side. You can pass them to [`action`/`stateAction`](/docs/define-actions/instance-methods#action--stateaction) method in the second argument, after the server code function. They don't return anything and can be async or not.

```tsx
import { actionClient } from "@/lib/safe-action";
Expand Down Expand Up @@ -53,12 +57,4 @@ const action = actionClient
hasNotFound
}) => {},
});
```

Here is the list of callbacks, with their behavior explained. All of them are optional and have return type `void` or `Promise<void>` (async or non-async functions with no return):

| Name | Executed after |
| ------------ | ----------------------------------------------------------------------- |
| `onSuccess?` | Action succeeded |
| `onError?` | Action errored with server or validation error(s) |
| `onSettled?` | Action succeeded or errored |
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Learn how to pass additional arguments to your actions.

Next.js allows you to [pass additional arguments to your actions by using the `bind` method](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#passing-additional-arguments). This method supports Progressive Enhancement.

next-safe-action exposes a [`bindArgsSchemas` method](/docs/safe-action-client/instance-methods#bindargsschemas) that expects an array of schemas for bind arguments.
next-safe-action exposes a [`bindArgsSchemas` method](/docs/define-actions/instance-methods#bindargsschemas) that expects an array of schemas for bind arguments.

For example, here we're going to define an `onboardUser` action that has `userId` and `age` as bind arguments and an object with an `username` property as the main argument:

Expand All @@ -24,13 +24,16 @@ const schema = z.object({
export const onboardUser = actionClient
.schema(schema)
// We can pass a named tuple type here, to get named parameters in the final function.
// highlight-start
.bindArgsSchemas<[userId: z.ZodString, age: z.ZodNumber]>([
z.string().uuid(),
z.number().min(18).max(150),
])
// highlight-end
.action(
async ({
parsedInput: { username },
// highlight-next-line
bindArgsParsedInputs: [userId, age],
}) => {
await new Promise((res) => setTimeout(res, 1000));
Expand All @@ -53,11 +56,15 @@ import { onboardUser } from "./onboard-action";
export default function OnboardPage() {
// Here we bind `userId` and `age` to `onboardUser`.
// `boundOnboardUser` will have just `{ username: string }` as its argument, after this `bind` call.
// highlight-start
const boundOnboardUser = onboardUser.bind(
null,
"d3a96f0f-e509-4f2f-b7d0-cdf50f0dc772",
30
);
// highlight-end

// highlight-next-line
const { execute, result, status, reset } = useAction(boundOnboardUser);

// ...
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,67 @@
---
sidebar_position: 1
description: You can initialize a safe action client with these options.
description: Learn how to create a safe action client,
---

# Initialization options
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

## `validationAdapter?`
# Create the client

This optional function is used to define which validation adapter should be used to validate the client input, based on the validation library of your choice. If not provided, the default `zodAdapter()` is used. The other two options, at this time, are `valibotAdapter()` and `yupAdapter()`. More information about that in the [validation libraries support](/docs/recipes/validation-libraries-support) recipe page.
After [installing the correct packages](/docs/getting-started#installation), the first thing you have to do is to create an instance of the safe action client, using the correct configuration based on your validation library of choice:

## `handleReturnedServerError?`
<Tabs>
<TabItem value="zod" label="Zod" default>
```typescript title="src/lib/safe-action.ts"
import { createSafeActionClient } from "next-safe-action";

export const actionClient = createSafeActionClient();
```

When using Zod, you don't need to specify a `validationAdapter`, because it's the default validation library for next-safe-action.
</TabItem>
<TabItem value="valibot" label="Valibot">
```typescript title="src/lib/safe-action.ts"
import { createSafeActionClient } from "next-safe-action";
import { valibotAdapter } from "next-safe-action/adapters/valibot";

export const actionClient = createSafeActionClient({
validationAdapter: valibotAdapter(),
});
```
</TabItem>
<TabItem value="yup" label="Yup">
```typescript title="src/lib/safe-action.ts"
import { createSafeActionClient } from "next-safe-action";
import { yupAdapter } from "next-safe-action/adapters/yup";

export const actionClient = createSafeActionClient({
validationAdapter: yupAdapter(),
});
```
</TabItem>
<TabItem value="typebox" label="TypeBox">
```typescript title="src/lib/safe-action.ts"
import { createSafeActionClient } from "next-safe-action";
import { typeboxAdapter } from "next-safe-action/adapters/typebox";

export const actionClient = createSafeActionClient({
validationAdapter: typeboxAdapter(),
});
```
</TabItem>
</Tabs>


In the following section we will go through all the options that can be passed to the `createSafeActionClient` function to customize the client behavior for your needs.

## Initialization options

### `validationAdapter?`

This optional function is used to define which validation adapter should be used to validate the client input, based on the validation library of your choice. If not provided, the default `zodAdapter()` is used.

### `handleReturnedServerError?`

This optional function is used to customize the server error returned to the client, if one occurs during action's server execution. This includes errors thrown by the action server code, and errors thrown by the middleware. You also have access to useful properties via the `utils` object, which is the second argument of the function.

Expand Down Expand Up @@ -53,7 +105,7 @@ export const actionClient = createSafeActionClient({
});
```

You can also easily rethrow all occurred server errors, if you prefer that behavior. This way, `serverError` in the [action result object](/docs/execution/action-result-object) will always be undefined and the action called from the client will throw the server error:
You can also easily rethrow all occurred server errors, if you prefer that behavior. This way, `serverError` in the [action result object](/docs/define-actions/action-result-object) will always be undefined and the action called from the client will throw the server error:

```typescript title=src/lib/safe-action.ts
import { createSafeActionClient } from "next-safe-action";
Expand All @@ -71,7 +123,7 @@ export const actionClient = createSafeActionClient({

Note that the return type of this function will determine the type of the server error that will be returned to the client. By default it is a string with the `DEFAULT_SERVER_ERROR_MESSAGE` for all errors.

## `handleServerErrorLog?`
### `handleServerErrorLog?`

This optional function is used to define how errors should be logged when one occurs while the server is executing an action. This includes errors thrown by the action server code, and errors thrown by the middleware. Here you get as the first argument the **original error object**, not the one customized by `handleReturnedServerError`, if provided. Though, you can access the `returnedError` and other useful properties inside the `utils` object, which is the second argument.

Expand All @@ -96,9 +148,9 @@ export const actionClient = createSafeActionClient({
});
```

## `defineMetadataSchema?`
### `defineMetadataSchema?`

This optional function is used to define the type of the metadata for safe actions. If not provided, `metadata` will default to `undefined` value. You can find more information about metadata in the [`metadata` instance method section](/docs/safe-action-client/instance-methods#metadata). If you define a metadata schema and you don't call the `metadata` method before defining an action, an error will be thrown.
This optional function is used to define the type of the metadata for safe actions. If not provided, `metadata` will default to `undefined` value. You can find more information about metadata in the [`metadata` instance method section](/docs/define-actions/instance-methods#metadata). If you define a metadata schema and you don't call the `metadata` method before defining an action, an error will be thrown.

Here's an example defining a client with a metadata object containing `actionName` as a string, using a Zod schema:

Expand All @@ -115,9 +167,9 @@ export const actionClient = createSafeActionClient({
});
```

## `defaultValidationErrorsShape?`
### `defaultValidationErrorsShape?`

This optional property is used to specify the default shape of the validation errors. The two possible values are `flattened` and `formatted`. The first one emulates Zod [`flatten`](https://zod.dev/ERROR_HANDLING?id=flattening-errors) method, the second one emulates Zod [`format`](https://zod.dev/ERROR_HANDLING?id=formatting-errors) method, both for `validationErrors` and `bindArgsValidationErrors`. You can override the default shape in `schema` and `bindArgsSchemas` methods, more information about that [here](/docs/recipes/customize-validation-errors-format). If this property is not provided, the default shape is `formatted`, as it also catches errors for nested schema objects.
This optional property is used to specify the default shape of the validation errors. The two possible values are `flattened` and `formatted`. The first one emulates Zod [`flatten`](https://zod.dev/ERROR_HANDLING?id=flattening-errors) method, the second one emulates Zod [`format`](https://zod.dev/ERROR_HANDLING?id=formatting-errors) method, both for `validationErrors` and `bindArgsValidationErrors`. You can override the default shape in `schema` and `bindArgsSchemas` methods, more information about that [here](/docs/define-actions/validation-errors#customize-validation-errors-format). If this property is not provided, the default shape is `formatted`, as it also catches errors for nested schema objects.

```typescript
import { createSafeActionClient } from "next-safe-action";
Expand All @@ -128,6 +180,6 @@ export const actionClient = createSafeActionClient({
});
```

## `throwValidationErrors?`
### `throwValidationErrors?`

This optional boolean property changes the default behavior of validation errors handling. When this option is set to `true`, the action will throw a `ActionValidationError` with the related validation errors in a `validationErrors` property. This option also works for server validation errors set with [`returnValidationErrors`](/docs/recipes/additional-validation-errors#returnvalidationerrors) function. The errors shape follows the `defaultValidationErrorsShape` option or the overridden one set in [`schema`](/docs/safe-action-client/instance-methods#schema) using the optional [`handleValidationErrorsShape`](/docs/recipes/customize-validation-errors-format) function. The default value is `false`.
This optional boolean property changes the default behavior of validation errors handling. When this option is set to `true`, the action will throw a `ActionValidationError` with the related validation errors in a `validationErrors` property. This option also works for server validation errors set with [`returnValidationErrors`](/docs/define-actions/validation-errors#returnvalidationerrors) function. The errors shape follows the `defaultValidationErrorsShape` option or the overridden one set in [`schema`](/docs/define-actions/instance-methods#schema) using the optional [`handleValidationErrorsShape`](/docs/define-actions/validation-errors#customize-validation-errors-format) function. The default value is `false`.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ sidebar_position: 6
description: Learn how to use next-safe-action with a i18n solution.
---

# Extend previous schema
# Extend previous schema(s)

Sometimes it's useful to define an action "template" with a base schema and then extend it with additional properties. This can be done inside the [`schema`](/docs/safe-action-client/instance-methods#schema) method by passing an async function that has the previous schema as its argument. See the example below:
Sometimes it's useful to define an action "template" with a base schema and then extend it with additional properties. This can be done inside the [`schema`](/docs/define-actions/instance-methods#schema) method by passing an async function that has the previous schema as its argument. See the example below:

```typescript
"use server";
Expand All @@ -19,6 +19,7 @@ const schema = z.object({

const myAction = actionClient
.schema(schema)
// highlight-start
.schema(async (prevSchema) => {
// Here we extend the previous schema with `password` property.
return prevSchema.extend({ password: z.string() });
Expand All @@ -27,6 +28,7 @@ const myAction = actionClient
// Here with `age` property.
return prevSchema.extend({ age: z.number().positive() });
})
// highlight-end
// `parsedInput` will be an object with `username`, `password` and `age` properties.
.action(async ({ parsedInput: { username, password, age } }) => {
// Do something useful here...
Expand Down
Loading

0 comments on commit a46b71a

Please sign in to comment.