Skip to content

Commit 3d677bd

Browse files
authoredApr 3, 2024··
feat: support generic server error (#93)
1 parent 2701376 commit 3d677bd

File tree

11 files changed

+161
-130
lines changed

11 files changed

+161
-130
lines changed
 

‎packages/example-app/src/lib/safe-action.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { randomUUID } from "crypto";
2-
import { DEFAULT_SERVER_ERROR, createSafeActionClient } from "next-safe-action";
2+
import {
3+
DEFAULT_SERVER_ERROR_MESSAGE,
4+
createSafeActionClient,
5+
} from "next-safe-action";
36

47
export class ActionError extends Error {}
58

@@ -20,7 +23,7 @@ export const action = createSafeActionClient({
2023
}
2124

2225
// Otherwise return default error message.
23-
return DEFAULT_SERVER_ERROR;
26+
return DEFAULT_SERVER_ERROR_MESSAGE;
2427
},
2528
}).use(async ({ next, metadata }) => {
2629
// Here we use a logging middleware.

‎packages/next-safe-action/src/hooks.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ const DEFAULT_RESULT = {
1616
fetchError: undefined,
1717
serverError: undefined,
1818
validationErrors: undefined,
19-
} satisfies HookResult<any, any>;
19+
} satisfies HookResult<any, any, any>;
2020

21-
const getActionStatus = <const S extends Schema, const Data>(
21+
const getActionStatus = <const ServerError, const S extends Schema, const Data>(
2222
isExecuting: boolean,
23-
result: HookResult<S, Data>
23+
result: HookResult<ServerError, S, Data>
2424
): HookActionStatus => {
2525
if (isExecuting) {
2626
return "executing";
@@ -37,12 +37,12 @@ const getActionStatus = <const S extends Schema, const Data>(
3737
return "idle";
3838
};
3939

40-
const useActionCallbacks = <const S extends Schema, const Data>(
41-
result: HookResult<S, Data>,
40+
const useActionCallbacks = <const ServerError, const S extends Schema, const Data>(
41+
result: HookResult<ServerError, S, Data>,
4242
input: InferIn<S>,
4343
status: HookActionStatus,
4444
reset: () => void,
45-
cb?: HookCallbacks<S, Data>
45+
cb?: HookCallbacks<ServerError, S, Data>
4646
) => {
4747
const onExecuteRef = React.useRef(cb?.onExecute);
4848
const onSuccessRef = React.useRef(cb?.onSuccess);
@@ -85,16 +85,16 @@ const useActionCallbacks = <const S extends Schema, const Data>(
8585
*
8686
* {@link https://next-safe-action.dev/docs/usage/client-components/hooks/useaction See an example}
8787
*/
88-
export const useAction = <const S extends Schema, const Data>(
89-
safeActionFn: SafeActionFn<S, Data>,
90-
callbacks?: HookCallbacks<S, Data>
88+
export const useAction = <const ServerError, const S extends Schema, const Data>(
89+
safeActionFn: SafeActionFn<ServerError, S, Data>,
90+
callbacks?: HookCallbacks<ServerError, S, Data>
9191
) => {
9292
const [, startTransition] = React.useTransition();
93-
const [result, setResult] = React.useState<HookResult<S, Data>>(DEFAULT_RESULT);
93+
const [result, setResult] = React.useState<HookResult<ServerError, S, Data>>(DEFAULT_RESULT);
9494
const [input, setInput] = React.useState<InferIn<S>>();
9595
const [isExecuting, setIsExecuting] = React.useState(false);
9696

97-
const status = getActionStatus<S, Data>(isExecuting, result);
97+
const status = getActionStatus<ServerError, S, Data>(isExecuting, result);
9898

9999
const execute = React.useCallback(
100100
(input: InferIn<S>) => {
@@ -144,14 +144,14 @@ export const useAction = <const S extends Schema, const Data>(
144144
*
145145
* {@link https://next-safe-action.dev/docs/usage/client-components/hooks/useoptimisticaction See an example}
146146
*/
147-
export const useOptimisticAction = <const S extends Schema, const Data>(
148-
safeActionFn: SafeActionFn<S, Data>,
147+
export const useOptimisticAction = <const ServerError, const S extends Schema, const Data>(
148+
safeActionFn: SafeActionFn<ServerError, S, Data>,
149149
initialOptimisticData: Data,
150150
reducer: (state: Data, input: InferIn<S>) => Data,
151-
callbacks?: HookCallbacks<S, Data>
151+
callbacks?: HookCallbacks<ServerError, S, Data>
152152
) => {
153153
const [, startTransition] = React.useTransition();
154-
const [result, setResult] = React.useState<HookResult<S, Data>>(DEFAULT_RESULT);
154+
const [result, setResult] = React.useState<HookResult<ServerError, S, Data>>(DEFAULT_RESULT);
155155
const [input, setInput] = React.useState<InferIn<S>>();
156156
const [isExecuting, setIsExecuting] = React.useState(false);
157157

@@ -160,7 +160,7 @@ export const useOptimisticAction = <const S extends Schema, const Data>(
160160
reducer
161161
);
162162

163-
const status = getActionStatus<S, Data>(isExecuting, result);
163+
const status = getActionStatus<ServerError, S, Data>(isExecuting, result);
164164

165165
const execute = React.useCallback(
166166
(input: InferIn<S>) => {

‎packages/next-safe-action/src/hooks.types.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,27 @@ import type { MaybePromise } from "./utils";
55
/**
66
* Type of `result` object returned by `useAction` and `useOptimisticAction` hooks.
77
*/
8-
export type HookResult<S extends Schema, Data> = SafeActionResult<S, Data> & {
8+
export type HookResult<ServerError, S extends Schema, Data> = SafeActionResult<
9+
ServerError,
10+
S,
11+
Data
12+
> & {
913
fetchError?: string;
1014
};
1115

1216
/**
1317
* Type of hooks callbacks. These are executed when action is in a specific state.
1418
*/
15-
export type HookCallbacks<S extends Schema, Data> = {
19+
export type HookCallbacks<ServerError, S extends Schema, Data> = {
1620
onExecute?: (input: InferIn<S>) => MaybePromise<void>;
1721
onSuccess?: (data: Data, input: InferIn<S>, reset: () => void) => MaybePromise<void>;
1822
onError?: (
19-
error: Omit<HookResult<S, Data>, "data">,
23+
error: Omit<HookResult<ServerError, S, Data>, "data">,
2024
input: InferIn<S>,
2125
reset: () => void
2226
) => MaybePromise<void>;
2327
onSettled?: (
24-
result: HookResult<S, Data>,
28+
result: HookResult<ServerError, S, Data>,
2529
input: InferIn<S>,
2630
reset: () => void
2731
) => MaybePromise<void>;

‎packages/next-safe-action/src/index.ts

+32-24
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,29 @@ import type {
1111
SafeActionResult,
1212
ServerCodeFn,
1313
} from "./index.types";
14-
import { DEFAULT_SERVER_ERROR, isError } from "./utils";
14+
import { DEFAULT_SERVER_ERROR_MESSAGE, isError } from "./utils";
1515
import {
1616
ServerValidationError,
1717
buildValidationErrors,
1818
returnValidationErrors,
1919
} from "./validation-errors";
2020
import type { ValidationErrors } from "./validation-errors.types";
2121

22-
class SafeActionClient<const Ctx = null> {
23-
private readonly handleServerErrorLog: NonNullable<SafeActionClientOpts["handleServerErrorLog"]>;
22+
class SafeActionClient<const ServerError, const Ctx = null> {
23+
private readonly handleServerErrorLog: NonNullable<
24+
SafeActionClientOpts<ServerError>["handleServerErrorLog"]
25+
>;
2426
private readonly handleReturnedServerError: NonNullable<
25-
SafeActionClientOpts["handleReturnedServerError"]
27+
SafeActionClientOpts<ServerError>["handleReturnedServerError"]
2628
>;
2729

28-
private middlewareFns: MiddlewareFn<any, any, any>[];
30+
private middlewareFns: MiddlewareFn<ServerError, any, any, any>[];
2931
private _metadata: ActionMetadata = {};
3032

3133
constructor(
32-
opts: { middlewareFns: MiddlewareFn<any, any, any>[] } & Required<SafeActionClientOpts>
34+
opts: { middlewareFns: MiddlewareFn<ServerError, any, any, any>[] } & Required<
35+
SafeActionClientOpts<ServerError>
36+
>
3337
) {
3438
this.middlewareFns = opts.middlewareFns;
3539
this.handleServerErrorLog = opts.handleServerErrorLog;
@@ -42,7 +46,7 @@ class SafeActionClient<const Ctx = null> {
4246
* @returns {SafeActionClient}
4347
*/
4448
public clone() {
45-
return new SafeActionClient<Ctx>({
49+
return new SafeActionClient<ServerError, Ctx>({
4650
handleReturnedServerError: this.handleReturnedServerError,
4751
handleServerErrorLog: this.handleServerErrorLog,
4852
middlewareFns: [...this.middlewareFns], // copy the middleware stack so we don't mutate it
@@ -55,11 +59,11 @@ class SafeActionClient<const Ctx = null> {
5559
* @returns SafeActionClient
5660
*/
5761
public use<const ClientInput, const NextCtx>(
58-
middlewareFn: MiddlewareFn<ClientInput, Ctx, NextCtx>
62+
middlewareFn: MiddlewareFn<ServerError, ClientInput, Ctx, NextCtx>
5963
) {
6064
this.middlewareFns.push(middlewareFn);
6165

62-
return new SafeActionClient<NextCtx>({
66+
return new SafeActionClient<ServerError, NextCtx>({
6367
middlewareFns: this.middlewareFns,
6468
handleReturnedServerError: this.handleReturnedServerError,
6569
handleServerErrorLog: this.handleServerErrorLog,
@@ -93,11 +97,13 @@ class SafeActionClient<const Ctx = null> {
9397
* @param serverCodeFn A function that executes the server code.
9498
* @returns {SafeActionFn}
9599
*/
96-
define<const Data = null>(serverCodeFn: ServerCodeFn<S, Data, Ctx>): SafeActionFn<S, Data> {
100+
define<const Data = null>(
101+
serverCodeFn: ServerCodeFn<S, Data, Ctx>
102+
): SafeActionFn<ServerError, S, Data> {
97103
return async (clientInput: unknown) => {
98104
let prevCtx: any = null;
99105
let frameworkError: Error | undefined = undefined;
100-
const middlewareResult: MiddlewareResult<any> = { success: false };
106+
const middlewareResult: MiddlewareResult<ServerError, any> = { success: false };
101107

102108
// Execute the middleware stack.
103109
const executeMiddlewareChain = async (idx = 0) => {
@@ -149,16 +155,14 @@ class SafeActionClient<const Ctx = null> {
149155
return;
150156
}
151157

152-
if (!isError(e)) {
153-
console.warn("Could not handle server error. Not an instance of Error: ", e);
154-
middlewareResult.serverError = DEFAULT_SERVER_ERROR;
155-
return;
156-
}
158+
// If error is not an instance of Error, wrap it in an Error object with
159+
// the default message.
160+
const error = isError(e) ? e : new Error(DEFAULT_SERVER_ERROR_MESSAGE);
157161

158-
await Promise.resolve(classThis.handleServerErrorLog(e));
162+
await Promise.resolve(classThis.handleServerErrorLog(error));
159163

160164
middlewareResult.serverError = await Promise.resolve(
161-
classThis.handleReturnedServerError(e)
165+
classThis.handleReturnedServerError(error)
162166
);
163167
}
164168
};
@@ -170,7 +174,7 @@ class SafeActionClient<const Ctx = null> {
170174
throw frameworkError;
171175
}
172176

173-
const actionResult: SafeActionResult<S, Data> = {};
177+
const actionResult: SafeActionResult<ServerError, S, Data> = {};
174178

175179
if (typeof middlewareResult.data !== "undefined") {
176180
actionResult.data = middlewareResult.data as Data;
@@ -199,7 +203,9 @@ class SafeActionClient<const Ctx = null> {
199203
*
200204
* {@link https://next-safe-action.dev/docs/getting-started See an example}
201205
*/
202-
export const createSafeActionClient = (createOpts?: SafeActionClientOpts) => {
206+
export const createSafeActionClient = <const ServerError = string>(
207+
createOpts?: SafeActionClientOpts<ServerError>
208+
) => {
203209
// If server log function is not provided, default to `console.error` for logging
204210
// server error messages.
205211
const handleServerErrorLog =
@@ -211,17 +217,19 @@ export const createSafeActionClient = (createOpts?: SafeActionClientOpts) => {
211217
// If `handleReturnedServerError` is provided, use it to handle server error
212218
// messages returned on the client.
213219
// Otherwise mask the error and use a generic message.
214-
const handleReturnedServerError = (e: Error) =>
215-
createOpts?.handleReturnedServerError?.(e) || DEFAULT_SERVER_ERROR;
220+
const handleReturnedServerError = ((e: Error) =>
221+
createOpts?.handleReturnedServerError?.(e) || DEFAULT_SERVER_ERROR_MESSAGE) as NonNullable<
222+
SafeActionClientOpts<ServerError>["handleReturnedServerError"]
223+
>;
216224

217-
return new SafeActionClient({
225+
return new SafeActionClient<ServerError, null>({
218226
middlewareFns: [async ({ next }) => next({ ctx: null })],
219227
handleServerErrorLog,
220228
handleReturnedServerError,
221229
});
222230
};
223231

224-
export { DEFAULT_SERVER_ERROR, returnValidationErrors, type ValidationErrors };
232+
export { DEFAULT_SERVER_ERROR_MESSAGE, returnValidationErrors, type ValidationErrors };
225233

226234
export type {
227235
ActionMetadata,

‎packages/next-safe-action/src/index.types.ts

+28-23
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,28 @@ import type { ValidationErrors } from "./validation-errors.types";
55
/**
66
* Type of options when creating a new safe action client.
77
*/
8-
export type SafeActionClientOpts = {
8+
export type SafeActionClientOpts<ServerError> = {
99
handleServerErrorLog?: (e: Error) => MaybePromise<void>;
10-
handleReturnedServerError?: (e: Error) => MaybePromise<string>;
10+
handleReturnedServerError?: (e: Error) => MaybePromise<ServerError>;
1111
};
1212

13+
/**
14+
* Type of the result of a safe action.
15+
*/
16+
// eslint-disable-next-line
17+
export type SafeActionResult<ServerError, S extends Schema, Data, NextCtx = unknown> = {
18+
data?: Data;
19+
serverError?: ServerError;
20+
validationErrors?: ValidationErrors<S>;
21+
};
22+
23+
/**
24+
* Type of the function called from components with typesafe input data.
25+
*/
26+
export type SafeActionFn<ServerError, S extends Schema, Data> = (
27+
input: InferIn<S>
28+
) => Promise<SafeActionResult<ServerError, S, Data>>;
29+
1330
/**
1431
* Type of meta options to be passed when defining a new safe action.
1532
*/
@@ -21,7 +38,12 @@ export type ActionMetadata = {
2138
* Type of the result of a middleware function. It extends the result of a safe action with
2239
* `parsedInput` and `ctx` optional properties.
2340
*/
24-
export type MiddlewareResult<NextCtx> = SafeActionResult<any, unknown, NextCtx> & {
41+
export type MiddlewareResult<ServerError, NextCtx> = SafeActionResult<
42+
ServerError,
43+
any,
44+
unknown,
45+
NextCtx
46+
> & {
2547
parsedInput?: unknown;
2648
ctx?: unknown;
2749
success: boolean;
@@ -30,15 +52,15 @@ export type MiddlewareResult<NextCtx> = SafeActionResult<any, unknown, NextCtx>
3052
/**
3153
* Type of the middleware function passed to a safe action client.
3254
*/
33-
export type MiddlewareFn<ClientInput, Ctx, NextCtx> = {
55+
export type MiddlewareFn<ServerError, ClientInput, Ctx, NextCtx> = {
3456
(opts: {
3557
clientInput: ClientInput;
3658
ctx: Ctx;
3759
metadata: ActionMetadata;
3860
next: {
39-
<const NC>(opts: { ctx: NC }): Promise<MiddlewareResult<NC>>;
61+
<const NC>(opts: { ctx: NC }): Promise<MiddlewareResult<ServerError, NC>>;
4062
};
41-
}): Promise<MiddlewareResult<NextCtx>>;
63+
}): Promise<MiddlewareResult<ServerError, NextCtx>>;
4264
};
4365

4466
/**
@@ -48,20 +70,3 @@ export type ServerCodeFn<S extends Schema, Data, Context> = (
4870
parsedInput: Infer<S>,
4971
utils: { ctx: Context; metadata: ActionMetadata }
5072
) => Promise<Data>;
51-
52-
/**
53-
* Type of the result of a safe action.
54-
*/
55-
// eslint-disable-next-line
56-
export type SafeActionResult<S extends Schema, Data, NextCtx = unknown> = {
57-
data?: Data;
58-
serverError?: string;
59-
validationErrors?: ValidationErrors<S>;
60-
};
61-
62-
/**
63-
* Type of the function called from components with typesafe input data.
64-
*/
65-
export type SafeActionFn<S extends Schema, Data> = (
66-
input: InferIn<S>
67-
) => Promise<SafeActionResult<S, Data>>;

‎packages/next-safe-action/src/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const DEFAULT_SERVER_ERROR = "Something went wrong while executing the operation.";
1+
export const DEFAULT_SERVER_ERROR_MESSAGE = "Something went wrong while executing the operation.";
22

33
export const isError = (error: unknown): error is Error => error instanceof Error;
44

‎website/docs/safe-action-client/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ Here's a reference of all the available optional functions:
1313

1414
| Function name | Purpose |
1515
|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
16-
| `handleReturnedServerError?` | When an error occurs on the server after executing the action on the client, it lets you define custom logic to returns a custom `serverError` message instead of the default one. More information [here](/docs/safe-action-client/initialization-options#handlereturnedservererror). |
16+
| `handleReturnedServerError?` | When an error occurs on the server after executing the action on the client, it lets you define custom logic to returns a custom `serverError` instead of the default string. More information [here](/docs/safe-action-client/initialization-options#handlereturnedservererror). |
1717
| `handleServerErrorLog?` | When an error occurs on the server after executing the action on the client, it lets you define custom logic to log the error on the server. By default the error is logged via `console.error`. More information [here](/docs/safe-action-client/initialization-options#handleservererrorlog). |

‎website/docs/safe-action-client/initialization-options.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ description: You can initialize a safe action client with these options.
77

88
## `handleReturnedServerError?`
99

10-
You can provide this optional function to the safe action client. It is used to customize the server error message 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.
10+
You can provide this optional function to the safe action client. It 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.
1111

12-
Here's a simple example, changing the message for every error thrown on the server:
12+
Here's a simple example, changing the default message for every error thrown on the server:
1313

1414
```typescript title=src/lib/safe-action.ts
1515
export const actionClient = createSafeActionClient({
@@ -25,7 +25,7 @@ export const actionClient = createSafeActionClient({
2525
A more useful one would be to customize the message based on the error type. We can, for instance, create a custom error class and check the error type inside this function:
2626

2727
```typescript title=src/lib/safe-action.ts
28-
import { DEFAULT_SERVER_ERROR } from "next-safe-action";
28+
import { DEFAULT_SERVER_ERROR_MESSAGE } from "next-safe-action";
2929

3030
class MyCustomError extends Error {}
3131

@@ -39,11 +39,13 @@ export const actionClient = createSafeActionClient({
3939
}
4040

4141
// Every other error that occurs will be masked with the default message.
42-
return DEFAULT_SERVER_ERROR;
42+
return DEFAULT_SERVER_ERROR_MESSAGE;
4343
},
4444
});
4545
```
4646

47+
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.
48+
4749
## `handleServerErrorLog?`
4850

4951
You can provide this optional function to the safe action client. This 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 argument the **original error object**, not a message customized by `handleReturnedServerError`, if provided.

‎website/docs/types.md

+61-52
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,34 @@ description: List of next-safe-action types.
1212
Type of options when creating a new safe action client.
1313

1414
```typescript
15-
export type SafeActionClientOpts = {
15+
export type SafeActionClientOpts<ServerError> = {
1616
handleServerErrorLog?: (e: Error) => MaybePromise<void>;
17-
handleReturnedServerError?: (e: Error) => MaybePromise<string>;
17+
handleReturnedServerError?: (e: Error) => MaybePromise<ServerError>;
1818
};
1919
```
2020

21+
### `SafeActionResult`
22+
23+
Type of the result of a safe action.
24+
25+
```typescript
26+
export type SafeActionResult<ServerError, S extends Schema, Data, NextCtx = unknown> = {
27+
data?: Data;
28+
serverError?: ServerError;
29+
validationErrors?: ValidationErrors<S>;
30+
};
31+
```
32+
33+
### `SafeActionFn`
34+
35+
Type of the function called from components with typesafe input data.
36+
37+
```typescript
38+
export type SafeActionFn<ServerError, S extends Schema, Data> = (
39+
input: InferIn<S>
40+
) => Promise<SafeActionResult<ServerError, S, Data>>;
41+
```
42+
2143
### `ActionMetadata`
2244

2345
Type of meta options to be passed when defining a new safe action.
@@ -33,10 +55,15 @@ export type ActionMetadata = {
3355
Type of the result of a middleware function. It extends the result of a safe action with `parsedInput` and `ctx` optional properties.
3456

3557
```typescript
36-
export type MiddlewareResult<NextCtx> = SafeActionResult<any, unknown, NextCtx> & {
37-
parsedInput?: unknown;
38-
ctx?: unknown;
39-
success: boolean;
58+
export type MiddlewareResult<ServerError, NextCtx> = SafeActionResult<
59+
ServerError,
60+
any,
61+
unknown,
62+
NextCtx
63+
> & {
64+
parsedInput?: unknown;
65+
ctx?: unknown;
66+
success: boolean;
4067
};
4168
```
4269

@@ -45,15 +72,15 @@ export type MiddlewareResult<NextCtx> = SafeActionResult<any, unknown, NextCtx>
4572
Type of the middleware function passed to a safe action client.
4673

4774
```typescript
48-
export type MiddlewareFn<ClientInput, Ctx, NextCtx> = {
49-
(opts: {
50-
clientInput: ClientInput;
51-
ctx: Ctx;
52-
metadata: ActionMetadata;
53-
next: {
54-
<const NC>(opts: { ctx: NC }): Promise<MiddlewareResult<NC>>;
55-
};
56-
}): Promise<MiddlewareResult<NextCtx>>;
75+
export type MiddlewareFn<ServerError, ClientInput, Ctx, NextCtx> = {
76+
(opts: {
77+
clientInput: ClientInput;
78+
ctx: Ctx;
79+
metadata: ActionMetadata;
80+
next: {
81+
<const NC>(opts: { ctx: NC }): Promise<MiddlewareResult<ServerError, NC>>;
82+
};
83+
}): Promise<MiddlewareResult<ServerError, NextCtx>>;
5784
};
5885
```
5986

@@ -76,28 +103,6 @@ Type of the returned object when input validation fails.
76103
export type ValidationErrors<S extends Schema> = Extend<ErrorList & SchemaErrors<Infer<S>>>;
77104
```
78105

79-
### `SafeActionResult`
80-
81-
Type of the result of a safe action.
82-
83-
```typescript
84-
export type SafeActionResult<S extends Schema, Data, NextCtx = unknown> = {
85-
data?: Data;
86-
serverError?: string;
87-
validationErrors?: ValidationErrors<S>;
88-
};
89-
```
90-
91-
### `SafeActionFn`
92-
93-
Type of the function called from components with typesafe input data.
94-
95-
```typescript
96-
export type SafeActionFn<S extends Schema, Data> = (
97-
input: InferIn<S>
98-
) => Promise<SafeActionResult<S, Data>>;
99-
```
100-
101106
## /hooks
102107

103108
### `HookResult`
@@ -107,8 +112,12 @@ Type of `result` object returned by `useAction` and `useOptimisticAction` hooks.
107112
If a server-client communication error occurs, `fetchError` will be set to the error message.
108113

109114
```typescript
110-
type HookResult<S extends Schema, Data> = SafeActionResult<S, Data> & {
111-
fetchError?: string;
115+
export type HookResult<ServerError, S extends Schema, Data> = SafeActionResult<
116+
ServerError,
117+
S,
118+
Data
119+
> & {
120+
fetchError?: string;
112121
};
113122
```
114123

@@ -117,19 +126,19 @@ type HookResult<S extends Schema, Data> = SafeActionResult<S, Data> & {
117126
Type of hooks callbacks. These are executed when action is in a specific state.
118127

119128
```typescript
120-
type HookCallbacks<S extends Schema, Data> = {
121-
onExecute?: (input: InferIn<S>) => MaybePromise<void>;
122-
onSuccess?: (data: Data, input: InferIn<S>, reset: () => void) => MaybePromise<void>;
123-
onError?: (
124-
error: Omit<HookResult<S, Data>, "data">,
125-
input: InferIn<S>,
126-
reset: () => void
127-
) => MaybePromise<void>;
128-
onSettled?: (
129-
result: HookResult<S, Data>,
130-
input: InferIn<S>,
131-
reset: () => void
132-
) => MaybePromise<void>;
129+
export type HookCallbacks<ServerError, S extends Schema, Data> = {
130+
onExecute?: (input: InferIn<S>) => MaybePromise<void>;
131+
onSuccess?: (data: Data, input: InferIn<S>, reset: () => void) => MaybePromise<void>;
132+
onError?: (
133+
error: Omit<HookResult<ServerError, S, Data>, "data">,
134+
input: InferIn<S>,
135+
reset: () => void
136+
) => MaybePromise<void>;
137+
onSettled?: (
138+
result: HookResult<ServerError, S, Data>,
139+
input: InferIn<S>,
140+
reset: () => void
141+
) => MaybePromise<void>;
133142
};
134143
```
135144

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ Here's how action result object is structured (all keys are optional):
1212
|--------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
1313
| `data?` | Execution is successful. | What you returned in action's server code. |
1414
| `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).
15-
| `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/initialization-options#handlereturnedservererror) when instantiating a new client. |
15+
| `serverError?` | An error occurs during action's server code execution. | A generic type that by default is the string "Something went wrong while executing the operation." for every server error that occurs, but this is [configurable](/docs/safe-action-client/initialization-options#handlereturnedservererror) when instantiating a new client. |

‎website/docs/usage/middleware.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Instance level is the right place when you want to share middleware behavior for
2020
Here we'll use a logging middleware in the base client and then extend it with an authorization middleware in `authActionClient`. We'll also define a safe action called `editProfile`, that will use `authActionClient` as its client. Note that the `handleReturnedServerError` function passed to the base client will also be used for `authActionClient`:
2121

2222
```typescript title="src/lib/safe-action.ts"
23-
import { createSafeActionClient } from "next-safe-action";
23+
import { createSafeActionClient, DEFAULT_SERVER_ERROR_MESSAGE } from "next-safe-action";
2424
import { cookies } from "next/headers";
2525
import { getUserIdFromSessionId } from "./db";
2626

@@ -33,7 +33,7 @@ const actionClient = createSafeActionClient({
3333
return e.message;
3434
}
3535

36-
return DEFAULT_SERVER_ERROR;
36+
return DEFAULT_SERVER_ERROR_MESSAGE;
3737
},
3838
// Define logging middleware.
3939
}).use(async ({ next, clientInput, metadata }) => {

0 commit comments

Comments
 (0)
Please sign in to comment.