Skip to content

Commit f8fe120

Browse files
authoredJun 26, 2024··
refactor: pass util props to handleReturnedServerError and handleServerErrorLog (#184)
This PR adds util props as the second argument of `handleReturnedServerError` and `handleServerErrorLog` optional init functions. Now you have access to `clientInput`, `bindArgsClientInputs`, `ctx`, `metadata` and also `returnedError` (just for `handleServerErrorLog`), which is the server error customized by `handleReturnedServerError` function. re #177
1 parent b3e78bb commit f8fe120

File tree

7 files changed

+107
-35
lines changed

7 files changed

+107
-35
lines changed
 

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

+24-4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ export function actionBuilder<
4141
handleBindArgsValidationErrorsShape: HandleBindArgsValidationErrorsShapeFn<BAS, CBAVE>;
4242
metadataSchema: MetadataSchema;
4343
metadata: MD;
44-
handleServerErrorLog: NonNullable<SafeActionClientOpts<ServerError, any, any>["handleServerErrorLog"]>;
45-
handleReturnedServerError: NonNullable<SafeActionClientOpts<ServerError, any, any>["handleReturnedServerError"]>;
44+
handleServerErrorLog: NonNullable<SafeActionClientOpts<ServerError, MetadataSchema, any>["handleServerErrorLog"]>;
45+
handleReturnedServerError: NonNullable<
46+
SafeActionClientOpts<ServerError, MetadataSchema, any>["handleReturnedServerError"]
47+
>;
4648
middlewareFns: MiddlewareFn<ServerError, any, any, any>[];
4749
ctxType: Ctx;
4850
validationStrategy: "typeschema" | "zod";
@@ -239,8 +241,26 @@ export function actionBuilder<
239241
// If error is not an instance of Error, wrap it in an Error object with
240242
// the default message.
241243
const error = isError(e) ? e : new Error(DEFAULT_SERVER_ERROR_MESSAGE);
242-
await Promise.resolve(args.handleServerErrorLog(error));
243-
middlewareResult.serverError = await Promise.resolve(args.handleReturnedServerError(error));
244+
const returnedError = await Promise.resolve(
245+
args.handleReturnedServerError(error, {
246+
clientInput: clientInputs.at(-1), // pass raw client input
247+
bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [],
248+
ctx: prevCtx,
249+
metadata: args.metadata as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,
250+
})
251+
);
252+
253+
middlewareResult.serverError = returnedError;
254+
255+
await Promise.resolve(
256+
args.handleServerErrorLog(error, {
257+
returnedError,
258+
clientInput: clientInputs.at(-1), // pass raw client input
259+
bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [],
260+
ctx: prevCtx,
261+
metadata: args.metadata as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,
262+
})
263+
);
244264
}
245265
}
246266
};

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

+9-8
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,18 @@ export const createSafeActionClient = <
4141
// server error messages.
4242
const handleServerErrorLog =
4343
createOpts?.handleServerErrorLog ||
44-
((e) => {
45-
console.error("Action error:", e.message);
46-
});
44+
(((originalError: Error) => {
45+
console.error("Action error:", originalError.message);
46+
}) as unknown as NonNullable<SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleServerErrorLog"]>);
4747

4848
// If `handleReturnedServerError` is provided, use it to handle server error
4949
// messages returned on the client.
5050
// Otherwise mask the error and use a generic message.
51-
const handleReturnedServerError = ((e: Error) =>
52-
createOpts?.handleReturnedServerError?.(e) || DEFAULT_SERVER_ERROR_MESSAGE) as NonNullable<
53-
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
54-
>;
51+
const handleReturnedServerError =
52+
createOpts?.handleReturnedServerError ||
53+
((() => DEFAULT_SERVER_ERROR_MESSAGE) as unknown as NonNullable<
54+
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
55+
>);
5556

5657
return new SafeActionClient({
5758
middlewareFns: [async ({ next }) => next({ ctx: undefined })],
@@ -61,7 +62,7 @@ export const createSafeActionClient = <
6162
schemaFn: undefined,
6263
bindArgsSchemas: [],
6364
ctxType: undefined,
64-
metadataSchema: createOpts?.defineMetadataSchema?.(),
65+
metadataSchema: (createOpts?.defineMetadataSchema?.() ?? undefined) as MetadataSchema,
6566
metadata: undefined as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,
6667
defaultValidationErrorsShape: (createOpts?.defaultValidationErrorsShape ?? "formatted") as ODVES,
6768
throwValidationErrors: Boolean(createOpts?.throwValidationErrors),

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

+20-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ import type { BindArgsValidationErrors, ValidationErrors } from "./validation-er
88
*/
99
export type DVES = "formatted" | "flattened";
1010

11+
/**
12+
* Type of the util properties passed to server error handler functions.
13+
*/
14+
export type ServerErrorFunctionUtils<MetadataSchema extends Schema | undefined> = {
15+
clientInput: unknown;
16+
bindArgsClientInputs: unknown[];
17+
ctx: unknown;
18+
metadata: MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined;
19+
};
20+
1121
/**
1222
* Type of options when creating a new safe action client.
1323
*/
@@ -16,9 +26,17 @@ export type SafeActionClientOpts<
1626
MetadataSchema extends Schema | undefined,
1727
ODVES extends DVES | undefined,
1828
> = {
19-
handleServerErrorLog?: (e: Error) => MaybePromise<void>;
20-
handleReturnedServerError?: (e: Error) => MaybePromise<ServerError>;
2129
defineMetadataSchema?: () => MetadataSchema;
30+
handleReturnedServerError?: (
31+
error: Error,
32+
utils: ServerErrorFunctionUtils<MetadataSchema>
33+
) => MaybePromise<ServerError>;
34+
handleServerErrorLog?: (
35+
originalError: Error,
36+
utils: ServerErrorFunctionUtils<MetadataSchema> & {
37+
returnedError: ServerError;
38+
}
39+
) => MaybePromise<void>;
2240
throwValidationErrors?: boolean;
2341
defaultValidationErrorsShape?: ODVES;
2442
};

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ export class SafeActionClient<
3131
const CBAVE = undefined,
3232
> {
3333
readonly #validationStrategy: "typeschema" | "zod";
34-
readonly #handleServerErrorLog: NonNullable<SafeActionClientOpts<ServerError, any, any>["handleServerErrorLog"]>;
34+
readonly #handleServerErrorLog: NonNullable<
35+
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleServerErrorLog"]
36+
>;
3537
readonly #handleReturnedServerError: NonNullable<
36-
SafeActionClientOpts<ServerError, any, any>["handleReturnedServerError"]
38+
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
3739
>;
3840
readonly #middlewareFns: MiddlewareFn<ServerError, any, any, any>[];
3941
readonly #ctxType = undefined as Ctx;
@@ -59,7 +61,7 @@ export class SafeActionClient<
5961
ctxType: Ctx;
6062
} & Required<
6163
Pick<
62-
SafeActionClientOpts<ServerError, any, ODVES>,
64+
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>,
6365
"handleReturnedServerError" | "handleServerErrorLog" | "defaultValidationErrorsShape" | "throwValidationErrors"
6466
>
6567
>

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

+11-9
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export type * from "./validation-errors.types";
2424

2525
/**
2626
* Create a new safe action client.
27-
* Note: this client only works with Zod as the validation library.
27+
* Note: this client is for validation libraries other than Zod, and can cause some problems with deployments, check out
28+
* [the troubleshooting page](https://next-safe-action.dev/docs/troubleshooting#typeschema-issues-with-edge-runtime) on the website.
2829
* If you want to use a validation library supported by [TypeSchema](https://typeschema.com), import this client from `/typeschema` path.
2930
* @param createOpts Optional initialization options
3031
*
@@ -41,17 +42,18 @@ export const createSafeActionClient = <
4142
// server error messages.
4243
const handleServerErrorLog =
4344
createOpts?.handleServerErrorLog ||
44-
((e) => {
45-
console.error("Action error:", e.message);
46-
});
45+
(((originalError: Error) => {
46+
console.error("Action error:", originalError.message);
47+
}) as unknown as NonNullable<SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleServerErrorLog"]>);
4748

4849
// If `handleReturnedServerError` is provided, use it to handle server error
4950
// messages returned on the client.
5051
// Otherwise mask the error and use a generic message.
51-
const handleReturnedServerError = ((e: Error) =>
52-
createOpts?.handleReturnedServerError?.(e) || DEFAULT_SERVER_ERROR_MESSAGE) as NonNullable<
53-
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
54-
>;
52+
const handleReturnedServerError =
53+
createOpts?.handleReturnedServerError ||
54+
((() => DEFAULT_SERVER_ERROR_MESSAGE) as unknown as NonNullable<
55+
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
56+
>);
5557

5658
return new SafeActionClient({
5759
middlewareFns: [async ({ next }) => next({ ctx: undefined })],
@@ -61,7 +63,7 @@ export const createSafeActionClient = <
6163
schemaFn: undefined,
6264
bindArgsSchemas: [],
6365
ctxType: undefined,
64-
metadataSchema: createOpts?.defineMetadataSchema?.(),
66+
metadataSchema: (createOpts?.defineMetadataSchema?.() ?? undefined) as MetadataSchema,
6567
metadata: undefined as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,
6668
defaultValidationErrorsShape: (createOpts?.defaultValidationErrorsShape ?? "formatted") as ODVES,
6769
throwValidationErrors: Boolean(createOpts?.throwValidationErrors),

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

+13-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ 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 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. You also have access to useful properties via the `utils` object, which is the second argument of the function.
1111

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

@@ -16,7 +16,10 @@ import { createSafeActionClient } from "next-safe-action";
1616

1717
export const actionClient = createSafeActionClient({
1818
// Can also be an async function.
19-
handleReturnedServerError(e) {
19+
handleReturnedServerError(e, utils) {
20+
// You can access these properties inside the `utils` object.
21+
const { clientInput, bindArgsClientInputs, metadata, ctx } = utils;
22+
2023
return "Oh no, something went wrong!";
2124
},
2225
});
@@ -66,7 +69,7 @@ Note that the return type of this function will determine the type of the server
6669

6770
## `handleServerErrorLog?`
6871

69-
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.
72+
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 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.
7073

7174
Here's a simple example, logging error to the console while also reporting it to an error handling system:
7275

@@ -75,12 +78,16 @@ import { createSafeActionClient } from "next-safe-action";
7578

7679
export const actionClient = createSafeActionClient({
7780
// Can also be an async function.
78-
handleServerErrorLog(e) {
81+
handleServerErrorLog(originalError, utils) {
82+
// You can access these properties inside the `utils` object.
83+
// Note that here you also have access to the custom server error defined by `handleReturnedServerError`.
84+
const { clientInput, bindArgsClientInputs, metadata, ctx, returnedError } = utils;
85+
7986
// We can, for example, also send the error to a dedicated logging system.
80-
reportToErrorHandlingSystem(e);
87+
reportToErrorHandlingSystem(originalError);
8188

8289
// And also log it to the console.
83-
console.error("Action error:", e.message);
90+
console.error("Action error:", originalError.message);
8491
},
8592
});
8693
```

‎website/docs/types.md

+25-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ Type of the default validation errors shape passed to `createSafeActionClient` v
1515
export type DVES = "flattened" | "formatted";
1616
```
1717

18+
### `ServerErrorFunctionUtils`
19+
20+
Type of the util properties passed to server error handler functions.
21+
22+
```typescript
23+
export type ServerErrorFunctionUtils<MetadataSchema extends Schema | undefined> = {
24+
clientInput: unknown;
25+
bindArgsClientInputs: unknown[];
26+
ctx: unknown;
27+
metadata: MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined;
28+
};
29+
```
30+
1831
### `SafeActionClientOpts`
1932

2033
Type of options when creating a new safe action client.
@@ -23,11 +36,20 @@ Type of options when creating a new safe action client.
2336
export type SafeActionClientOpts<
2437
ServerError,
2538
MetadataSchema extends Schema | undefined,
26-
ODVES extends DVES | undefined
39+
ODVES extends DVES | undefined,
2740
> = {
28-
handleServerErrorLog?: (e: Error) => MaybePromise<void>;
29-
handleReturnedServerError?: (e: Error) => MaybePromise<ServerError>;
3041
defineMetadataSchema?: () => MetadataSchema;
42+
handleReturnedServerError?: (
43+
error: Error,
44+
utils: ServerErrorFunctionUtils<MetadataSchema>
45+
) => MaybePromise<ServerError>;
46+
handleServerErrorLog?: (
47+
originalError: Error,
48+
utils: ServerErrorFunctionUtils<MetadataSchema> & {
49+
returnedError: ServerError;
50+
}
51+
) => MaybePromise<void>;
52+
throwValidationErrors?: boolean;
3153
defaultValidationErrorsShape?: ODVES;
3254
};
3355
```

0 commit comments

Comments
 (0)
Please sign in to comment.