T
TanStack3mo ago
funny-blue

Show API validations in the Form

I validate my forms on the backend, here's an example
{
"json": {
"defined": false,
"code": "INPUT_VALIDATION_FAILED",
"status": 422,
"message": "INPUT_VALIDATION_FAILED",
"data": {
"errors": [],
"properties": {
"name": {
"errors": [
"Name must be at least 1 characters"
]
}
}
}
}
}
{
"json": {
"defined": false,
"code": "INPUT_VALIDATION_FAILED",
"status": 422,
"message": "INPUT_VALIDATION_FAILED",
"data": {
"errors": [],
"properties": {
"name": {
"errors": [
"Name must be at least 1 characters"
]
}
}
}
}
}
Now I want to push the errors on the form, but I'm not sure which api to use
function RouteComponent() {
const {
data: {user},
} = useSuspenseQuery(orpc.auth.getSession.queryOptions());

const updateNameMutation = useMutation(
orpc.user.updateName.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries(orpc.auth.getSession.queryOptions());
toast.success("Your name has been updated");
},
}),
);

const form = useForm({
defaultValues: {name: user?.name || ""},
onSubmit: async ({value: {name}}) => {
updateNameMutation.mutate({name});
},
});

return <div>...</div>
}
function RouteComponent() {
const {
data: {user},
} = useSuspenseQuery(orpc.auth.getSession.queryOptions());

const updateNameMutation = useMutation(
orpc.user.updateName.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries(orpc.auth.getSession.queryOptions());
toast.success("Your name has been updated");
},
}),
);

const form = useForm({
defaultValues: {name: user?.name || ""},
onSubmit: async ({value: {name}}) => {
updateNameMutation.mutate({name});
},
});

return <div>...</div>
}
2 Replies
quickest-silver
quickest-silver3mo ago
There's a WIP PR to clarify Submission Handling, here's a Stackblitz that showcases it
funny-blue
funny-blueOP3mo ago
checking, thanks! i think that settles it, thanks a lot @ksgn for what it matters here's what I do now
const form = useForm({
defaultValues: {name: organization.name},
validators: {
onSubmit: z.object({
name: z
.string()
.trim()
.min(2, "Name must be at least 2 characters")
.max(100, "Name must be less than 100 characters"),
}),
onSubmitAsync: ({value: {name}}) => {
return withValidationErrors(
updateOrganizationNameMutation.mutateAsync({
name,
organizationId: orgId,
}),
);
},
},
});
const form = useForm({
defaultValues: {name: organization.name},
validators: {
onSubmit: z.object({
name: z
.string()
.trim()
.min(2, "Name must be at least 2 characters")
.max(100, "Name must be less than 100 characters"),
}),
onSubmitAsync: ({value: {name}}) => {
return withValidationErrors(
updateOrganizationNameMutation.mutateAsync({
name,
organizationId: orgId,
}),
);
},
},
});
export const mapValidationErrors = (errors: Record<string, Array<string>>) => {
const mappedErrors: Record<string, Error> = {};

Object.entries(errors).forEach(([key, messages]) => {
mappedErrors[key] = new Error(messages[0]);
});

return mappedErrors;
};

export function withValidationErrors<T>(
promise: Promise<T>,
): Promise<T | {fields: Record<string, Error>}> {
return promise.catch((error) => {
if (error.code === "INPUT_VALIDATION_FAILED" && error.data?.fieldErrors) {
return {fields: mapValidationErrors(error.data.fieldErrors)};
}

throw error;
});
}
export const mapValidationErrors = (errors: Record<string, Array<string>>) => {
const mappedErrors: Record<string, Error> = {};

Object.entries(errors).forEach(([key, messages]) => {
mappedErrors[key] = new Error(messages[0]);
});

return mappedErrors;
};

export function withValidationErrors<T>(
promise: Promise<T>,
): Promise<T | {fields: Record<string, Error>}> {
return promise.catch((error) => {
if (error.code === "INPUT_VALIDATION_FAILED" && error.data?.fieldErrors) {
return {fields: mapValidationErrors(error.data.fieldErrors)};
}

throw error;
});
}

Did you find this page helpful?