next-safe-action-query
Version:
Type-safe server actions with built-in validation for Next.js
78 lines (77 loc) • 2.17 kB
JavaScript
"use client";
import {
useQuery
} from "@tanstack/react-query";
class SafeActionError extends Error {
constructor(message, type, details) {
super(message);
this.type = type;
this.details = details;
this.name = "SafeActionError";
}
}
function useSafeActionQuery(queryKey, action, options) {
const {
actionInput,
onServerError,
onValidationErrors,
onNetworkError,
...queryOptions
} = options;
return useQuery({
queryKey,
queryFn: async () => {
try {
const result = await action(actionInput);
if (result?.serverError) {
onServerError?.(result.serverError);
throw new SafeActionError(result.serverError, "server");
}
if (result?.validationErrors) {
const errors = result.validationErrors._errors ?? [];
onValidationErrors?.(errors);
throw new SafeActionError(
errors.join(", ") || "Validation failed",
"validation",
result.validationErrors
);
}
if (result?.data === void 0 || result?.data === null) {
throw new SafeActionError("No data returned from action", "server");
}
return result.data;
} catch (error) {
if (error instanceof SafeActionError) {
throw error;
}
const networkError = error instanceof Error ? error : new Error("Unknown error occurred");
onNetworkError?.(networkError);
throw new SafeActionError(
`Network error: ${networkError.message}`,
"network",
networkError
);
}
},
retry: (failureCount, error) => {
if (error instanceof SafeActionError && error.type === "validation") {
return false;
}
if (error instanceof SafeActionError && error.type === "server") {
return false;
}
return failureCount < 3;
},
// Set reasonable defaults
staleTime: 30 * 1e3,
// 30 seconds default
throwOnError: false,
// Return errors as state by default
...queryOptions
});
}
export {
SafeActionError,
useSafeActionQuery
};
//# sourceMappingURL=index.js.map