Skip to content

Commit c900720

Browse files
authoredSep 4, 2024··
feat: merge server error handling functions into handleServerError (#257)
Code in this PR merges the functionality of `handleServerErrorLog` and `handleReturnedServerError` functions into a single optional initialization function called `handleServerError`. This change has been made because having two functions for server error handling is unnecessary, you can easily manage both logging and returned error within a single function.
1 parent 4a74df9 commit c900720

15 files changed

+79
-132
lines changed
 

‎apps/playground/src/lib/safe-action.ts

+5-10
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,11 @@ export class ActionError extends Error {}
99

1010
export const action = createSafeActionClient({
1111
validationAdapter: zodAdapter(),
12-
// You can provide a custom logging function, otherwise the lib will use `console.error`
13-
// as the default logging system. If you want to disable server errors logging,
14-
// just pass an empty Promise.
15-
handleServerErrorLog: (e) => {
16-
console.error(
17-
"CUSTOM ERROR LOG FUNCTION, server error message:",
18-
e.message
19-
);
20-
},
21-
handleReturnedServerError: (e) => {
12+
// You can provide a custom handler for server errors, otherwise the lib will use `console.error`
13+
// as the default logging mechanism and will return the DEFAULT_SERVER_ERROR_MESSAGE for all server errors.
14+
handleServerError: (e) => {
15+
console.error("Action server error occurred:", e.message);
16+
2217
// If the error is an instance of `ActionError`, unmask the message.
2318
if (e instanceof ActionError) {
2419
return e.message;

‎packages/next-safe-action/src/__tests__/metadata.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { zodAdapter } from "../adapters/zod";
88

99
const ac = createSafeActionClient({
1010
validationAdapter: zodAdapter(),
11-
handleServerErrorLog() {}, // disable server errors logging for these tests
11+
handleServerError: () => DEFAULT_SERVER_ERROR_MESSAGE, // disable server errors logging for these tests
1212
defineMetadataSchema() {
1313
return z.object({
1414
actionName: z.string(),

‎packages/next-safe-action/src/__tests__/middleware.test.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { z } from "zod";
66
import {
77
createMiddleware,
88
createSafeActionClient,
9+
DEFAULT_SERVER_ERROR_MESSAGE,
910
formatBindArgsValidationErrors,
1011
formatValidationErrors,
1112
returnValidationErrors,
@@ -14,8 +15,8 @@ import { zodAdapter } from "../adapters/zod";
1415

1516
const ac = createSafeActionClient({
1617
validationAdapter: zodAdapter(),
17-
handleServerErrorLog() {}, // disable server errors logging for these tests
18-
handleReturnedServerError(e) {
18+
handleServerError(e) {
19+
// disable server error logging for these tests
1920
return {
2021
message: e.message,
2122
};
@@ -296,7 +297,7 @@ test("server validation errors in execution result from middleware are correct",
296297

297298
const flac = createSafeActionClient({
298299
validationAdapter: zodAdapter(),
299-
handleServerErrorLog() {}, // disable server errors logging for these tests
300+
handleServerError: () => DEFAULT_SERVER_ERROR_MESSAGE, // disable server errors logging for these tests
300301
defaultValidationErrorsShape: "flattened",
301302
});
302303

‎packages/next-safe-action/src/__tests__/server-error.test.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ class ActionError extends Error {
1313

1414
const ac1 = createSafeActionClient({
1515
validationAdapter: zodAdapter(),
16-
handleServerErrorLog: () => {}, // disable server errors logging for these tests
17-
handleReturnedServerError(e) {
16+
handleServerError(e) {
17+
// disable server error logging for these tests
1818
if (e instanceof ActionError) {
1919
return e.message;
2020
}
@@ -107,15 +107,15 @@ test("error occurred with `throwServerError` set to true at the action level thr
107107
// Server error is an object with a 'message' property.
108108
const ac2 = createSafeActionClient({
109109
validationAdapter: zodAdapter(),
110-
handleServerErrorLog: () => {}, // disable server errors logging for these tests
111-
handleReturnedServerError(e) {
110+
handleServerError(e) {
111+
// disable server errors logging for these tests
112112
return {
113113
message: e.message,
114114
};
115115
},
116116
});
117117

118-
test("error occurred in server code function has the correct shape defined by `handleReturnedServerError`", async () => {
118+
test("error occurred in server code function has the correct shape defined by `handleServerError`", async () => {
119119
const action = ac2.action(async () => {
120120
throw new Error("Something bad happened");
121121
});
@@ -129,7 +129,7 @@ test("error occurred in server code function has the correct shape defined by `h
129129
assert.deepStrictEqual(actualResult, expectedResult);
130130
});
131131

132-
test("error occurred in middleware function has the correct shape defined by `handleReturnedServerError`", async () => {
132+
test("error occurred in middleware function has the correct shape defined by `handleServerError`", async () => {
133133
const action = ac2
134134
.use(async ({ next }) => next())
135135
.use(async () => {
@@ -153,21 +153,21 @@ test("error occurred in middleware function has the correct shape defined by `ha
153153
// Rethrow all server errors.
154154
const ac3 = createSafeActionClient({
155155
validationAdapter: zodAdapter(),
156-
handleServerErrorLog: () => {}, // disable server errors logging for these tests
157-
handleReturnedServerError(e) {
156+
handleServerError(e) {
157+
// disable server error logging for these tests
158158
throw e;
159159
},
160160
});
161161

162-
test("action throws if an error occurred in server code function and `handleReturnedServerError` rethrows it", async () => {
162+
test("action throws if an error occurred in server code function and `handleServerError` rethrows it", async () => {
163163
const action = ac3.action(async () => {
164164
throw new Error("Something bad happened");
165165
});
166166

167167
assert.rejects(() => action());
168168
});
169169

170-
test("action throws if an error occurred in middleware function and `handleReturnedServerError` rethrows it", async () => {
170+
test("action throws if an error occurred in middleware function and `handleServerError` rethrows it", async () => {
171171
const action = ac3
172172
.use(async ({ next }) => next())
173173
.use(async () => {

‎packages/next-safe-action/src/__tests__/validation-errors.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ test("action with invalid output data returns the default `serverError`", async
171171
test("action with invalid output data throws an error of the correct type", async () => {
172172
const tac = createSafeActionClient({
173173
validationAdapter: zodAdapter(),
174-
handleReturnedServerError: (e) => {
174+
handleServerError: (e) => {
175+
// disable server error logging for this test
175176
throw e;
176177
},
177178
});

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

+2-15
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,7 @@ export function actionBuilder<
5050
handleBindArgsValidationErrorsShape: HandleBindArgsValidationErrorsShapeFn<BAS, CBAVE>;
5151
metadataSchema: MetadataSchema;
5252
metadata: MD;
53-
handleServerErrorLog: NonNullable<SafeActionClientOpts<ServerError, MetadataSchema, any>["handleServerErrorLog"]>;
54-
handleReturnedServerError: NonNullable<
55-
SafeActionClientOpts<ServerError, MetadataSchema, any>["handleReturnedServerError"]
56-
>;
53+
handleServerError: NonNullable<SafeActionClientOpts<ServerError, MetadataSchema, any>["handleServerError"]>;
5754
middlewareFns: MiddlewareFn<ServerError, any, any, any>[];
5855
ctxType: Ctx;
5956
throwValidationErrors: boolean;
@@ -250,7 +247,7 @@ export function actionBuilder<
250247
// the default message.
251248
const error = isError(e) ? e : new Error(DEFAULT_SERVER_ERROR_MESSAGE);
252249
const returnedError = await Promise.resolve(
253-
args.handleReturnedServerError(error, {
250+
args.handleServerError(error, {
254251
clientInput: clientInputs.at(-1), // pass raw client input
255252
bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [],
256253
ctx: currentCtx,
@@ -259,16 +256,6 @@ export function actionBuilder<
259256
);
260257

261258
middlewareResult.serverError = returnedError;
262-
263-
await Promise.resolve(
264-
args.handleServerErrorLog(error, {
265-
returnedError,
266-
clientInput: clientInputs.at(-1), // pass raw client input
267-
bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [],
268-
ctx: currentCtx,
269-
metadata: args.metadata as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,
270-
})
271-
);
272259
}
273260
}
274261
};

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

+8-18
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,17 @@ export const createSafeActionClient = <
4040
>(
4141
createOpts?: SafeActionClientOpts<ServerError, MetadataSchema, ODVES>
4242
) => {
43-
// If server log function is not provided, default to `console.error` for logging
44-
// server error messages.
45-
const handleServerErrorLog =
46-
createOpts?.handleServerErrorLog ||
47-
(((originalError: Error) => {
48-
console.error("Action error:", originalError.message);
49-
}) as unknown as NonNullable<SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleServerErrorLog"]>);
50-
51-
// If `handleReturnedServerError` is provided, use it to handle server error
52-
// messages returned on the client.
53-
// Otherwise mask the error and use a generic message.
54-
const handleReturnedServerError =
55-
createOpts?.handleReturnedServerError ||
56-
((() => DEFAULT_SERVER_ERROR_MESSAGE) as unknown as NonNullable<
57-
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
58-
>);
43+
// If `handleServerError` is provided, use it, otherwise default to log to console and generic error message.
44+
const handleServerError: NonNullable<SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleServerError"]> =
45+
createOpts?.handleServerError ||
46+
((e) => {
47+
console.error("Action error:", e.message);
48+
return DEFAULT_SERVER_ERROR_MESSAGE as ServerError;
49+
});
5950

6051
return new SafeActionClient({
6152
middlewareFns: [async ({ next }) => next({ ctx: {} })],
62-
handleServerErrorLog,
63-
handleReturnedServerError,
53+
handleServerError,
6454
inputSchemaFn: undefined,
6555
bindArgsSchemas: [],
6656
outputSchema: undefined,

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

+1-10
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,7 @@ export type SafeActionClientOpts<
2929
> = {
3030
validationAdapter?: ValidationAdapter;
3131
defineMetadataSchema?: () => MetadataSchema;
32-
handleReturnedServerError?: (
33-
error: Error,
34-
utils: ServerErrorFunctionUtils<MetadataSchema>
35-
) => MaybePromise<ServerError>;
36-
handleServerErrorLog?: (
37-
originalError: Error,
38-
utils: ServerErrorFunctionUtils<MetadataSchema> & {
39-
returnedError: ServerError;
40-
}
41-
) => MaybePromise<void>;
32+
handleServerError?: (error: Error, utils: ServerErrorFunctionUtils<MetadataSchema>) => MaybePromise<ServerError>;
4233
throwValidationErrors?: boolean;
4334
defaultValidationErrorsShape?: ODVES;
4435
};

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

+11-22
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,8 @@ export class SafeActionClient<
3131
CVE = undefined,
3232
const CBAVE = undefined,
3333
> {
34-
readonly #handleServerErrorLog: NonNullable<
35-
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleServerErrorLog"]
36-
>;
37-
readonly #handleReturnedServerError: NonNullable<
38-
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
34+
readonly #handleServerError: NonNullable<
35+
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleServerError"]
3936
>;
4037
readonly #middlewareFns: MiddlewareFn<ServerError, any, any, any>[];
4138
readonly #metadataSchema: MetadataSchema;
@@ -65,13 +62,12 @@ export class SafeActionClient<
6562
} & Required<
6663
Pick<
6764
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>,
68-
"handleReturnedServerError" | "handleServerErrorLog" | "defaultValidationErrorsShape" | "throwValidationErrors"
65+
"handleServerError" | "defaultValidationErrorsShape" | "throwValidationErrors"
6966
>
7067
>
7168
) {
7269
this.#middlewareFns = opts.middlewareFns;
73-
this.#handleServerErrorLog = opts.handleServerErrorLog;
74-
this.#handleReturnedServerError = opts.handleReturnedServerError;
70+
this.#handleServerError = opts.handleServerError;
7571
this.#metadataSchema = opts.metadataSchema;
7672
this.#metadata = opts.metadata;
7773
this.#inputSchemaFn = (opts.inputSchemaFn ?? undefined) as ISF;
@@ -94,8 +90,7 @@ export class SafeActionClient<
9490
use<NextCtx extends object>(middlewareFn: MiddlewareFn<ServerError, MD, Ctx, Ctx & NextCtx>) {
9591
return new SafeActionClient({
9692
middlewareFns: [...this.#middlewareFns, middlewareFn],
97-
handleReturnedServerError: this.#handleReturnedServerError,
98-
handleServerErrorLog: this.#handleServerErrorLog,
93+
handleServerError: this.#handleServerError,
9994
metadataSchema: this.#metadataSchema,
10095
metadata: this.#metadata,
10196
inputSchemaFn: this.#inputSchemaFn,
@@ -119,8 +114,7 @@ export class SafeActionClient<
119114
metadata(data: MD) {
120115
return new SafeActionClient({
121116
middlewareFns: this.#middlewareFns,
122-
handleReturnedServerError: this.#handleReturnedServerError,
123-
handleServerErrorLog: this.#handleServerErrorLog,
117+
handleServerError: this.#handleServerError,
124118
metadataSchema: this.#metadataSchema,
125119
metadata: data,
126120
inputSchemaFn: this.#inputSchemaFn,
@@ -154,8 +148,7 @@ export class SafeActionClient<
154148
) {
155149
return new SafeActionClient({
156150
middlewareFns: this.#middlewareFns,
157-
handleReturnedServerError: this.#handleReturnedServerError,
158-
handleServerErrorLog: this.#handleServerErrorLog,
151+
handleServerError: this.#handleServerError,
159152
metadataSchema: this.#metadataSchema,
160153
metadata: this.#metadata,
161154
// @ts-expect-error
@@ -196,8 +189,7 @@ export class SafeActionClient<
196189
) {
197190
return new SafeActionClient({
198191
middlewareFns: this.#middlewareFns,
199-
handleReturnedServerError: this.#handleReturnedServerError,
200-
handleServerErrorLog: this.#handleServerErrorLog,
192+
handleServerError: this.#handleServerError,
201193
metadataSchema: this.#metadataSchema,
202194
metadata: this.#metadata,
203195
inputSchemaFn: this.#inputSchemaFn,
@@ -222,8 +214,7 @@ export class SafeActionClient<
222214
outputSchema<OOS extends Schema>(dataSchema: OOS) {
223215
return new SafeActionClient({
224216
middlewareFns: this.#middlewareFns,
225-
handleReturnedServerError: this.#handleReturnedServerError,
226-
handleServerErrorLog: this.#handleServerErrorLog,
217+
handleServerError: this.#handleServerError,
227218
metadataSchema: this.#metadataSchema,
228219
metadata: this.#metadata,
229220
inputSchemaFn: this.#inputSchemaFn,
@@ -250,8 +241,7 @@ export class SafeActionClient<
250241
utils?: SafeActionUtils<ServerError, MD, Ctx, IS, BAS, CVE, CBAVE, Data>
251242
) {
252243
return actionBuilder({
253-
handleReturnedServerError: this.#handleReturnedServerError,
254-
handleServerErrorLog: this.#handleServerErrorLog,
244+
handleServerError: this.#handleServerError,
255245
middlewareFns: this.#middlewareFns,
256246
ctxType: this.#ctxType,
257247
metadataSchema: this.#metadataSchema,
@@ -279,8 +269,7 @@ export class SafeActionClient<
279269
utils?: SafeActionUtils<ServerError, MD, Ctx, IS, BAS, CVE, CBAVE, Data>
280270
) {
281271
return actionBuilder({
282-
handleReturnedServerError: this.#handleReturnedServerError,
283-
handleServerErrorLog: this.#handleServerErrorLog,
272+
handleServerError: this.#handleServerError,
284273
middlewareFns: this.#middlewareFns,
285274
ctxType: this.#ctxType,
286275
metadataSchema: this.#metadataSchema,

‎website/docs/define-actions/action-result-object.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ Here's how action result object is structured (all keys are optional):
1010
- `data`: when execution is successful, what you returned in action's server code.
1111
- `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).
1212
- `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).
13-
- `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.
13+
- `serverError`: when execution fails, an error object that contains the error message, customizable by using the [`handleServerError`](/docs/define-actions/create-the-client#handleservererror) initialization function.

0 commit comments

Comments
 (0)
Please sign in to comment.