T
TanStack4w ago
exotic-emerald

How to use TanStack Start's `createServerFn`, validation library, & Form

I have a form that submits data to the server via TSS's createServerFn function:
const fooSchema = v.object({
foo: v.string(),
});

export const doSomethingOnTheServer = createServerFn({ method: "POST" })
.validator(fooSchema)
.handler(async ({ data: foo }) => {
return await doSomething(foo);
});

const Form = () => {
const updateFooMutation = useMutation({
mutationFn: async (value: v.InferOutput<typeof fooSchema>) => {
doSomethingOnTheServer({ data: value });
},
});

// implements createFormHook
const fooForm = useAppForm({
defaultValues: {
foo: "bar",
},
validators: {
onChange: fooSchema,
},
onSubmit: async ({ formApi, value }) => {
await updateFooMutation.mutateAsync(value);

formApi.reset();
},
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
fooForm.handleSubmit();
}}
>
<fooForm.AppField children={(field) => <field.TextField label="Foo" />} name="foo" />
</form>
);
};
const fooSchema = v.object({
foo: v.string(),
});

export const doSomethingOnTheServer = createServerFn({ method: "POST" })
.validator(fooSchema)
.handler(async ({ data: foo }) => {
return await doSomething(foo);
});

const Form = () => {
const updateFooMutation = useMutation({
mutationFn: async (value: v.InferOutput<typeof fooSchema>) => {
doSomethingOnTheServer({ data: value });
},
});

// implements createFormHook
const fooForm = useAppForm({
defaultValues: {
foo: "bar",
},
validators: {
onChange: fooSchema,
},
onSubmit: async ({ formApi, value }) => {
await updateFooMutation.mutateAsync(value);

formApi.reset();
},
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
fooForm.handleSubmit();
}}
>
<fooForm.AppField children={(field) => <field.TextField label="Foo" />} name="foo" />
</form>
);
};
Notice how the same schema is used in the validators.onChange field in the form and in the validator() function on the createServerFn chain. I want to handle errors that come from the createServerFn().validator() function properly in the form. But from what I gather from the docs, I'm not doing this correctly. From what I can gather from the docs, what I'm supposed to do is to first validate the data inside validators.onSubmitAsync, and then the submit function will go through. https://tanstack.com/form/latest/docs/framework/react/guides/validation#setting-field-level-errors-from-the-forms-validators Now if this is correct, I feel like this kind of wonky. I already have a validator on my server function that runs my validation on the server, and setting up another server function call to validate the data seems unnecessary. Is there a way to make this all play nice together?
Form and Field Validation | TanStack Form React Docs
At the core of TanStack Form's functionalities is the concept of validation. TanStack Form makes validation highly customizable: You can control when to perform the validation (on change, on input, on...
4 Replies
exotic-emerald
exotic-emeraldOP4w ago
If not, the errors I get back from the server include the key of the form name on which I can set a field-specific error. Is it possible to set that error from outside the validators.onSubmitAsync field? I don't see anything in the docs on how to handle that.
absent-sapphire
absent-sapphire4w ago
onSubmitAsync will only be called once during submission, so it's allowed to mutate data on success if you have no differentiation between validator endpoints and mutating endpoints, it's fine to keep them in onSubmitAsync. onSubmit itself is not needed in that case (unless you want to handle success cases there, but using the mutation will also do). Keep in mind that mutateAsync throws by default, so you should catch that.
exotic-emerald
exotic-emeraldOP4w ago
Yeah, no differentiation between validator endpoints and mutating endpoints, I like how the API for createServerFn lets you handle them both at once. I guess I'm still a bit lost on how to handle validation errors from the server in this case. Do I just ditch the useMutation() handlers and handle everything inside onSubmitAsync()? To illustrate, here's the same example, but there's a schema for client validation & server validation.
const fooSchemaClient = v.object({
foo: v.string('client message'),
});

const fooSchemaServer = v.object({
foo: v.string('server message'),
});

export const doSomethingOnTheServer = createServerFn({ method: "POST" })
.validator(fooSchemaServer)
.handler(async ({ data: foo }) => {
return await doSomething(foo);
});

const Form = () => {
const updateFooMutation = useMutation({
mutationFn: async (value: v.InferOutput<typeof fooSchemaClient>) => {
doSomethingOnTheServer({ data: value });
},
onError: (error) => {
console.error("Do something with the error", error);
},
});

const fooForm = useAppForm({
defaultValues: {
foo: "bar",
},
validators: {
onChange: fooSchemaClient,
},
onSubmit: ({ value }) => updateFooMutation.mutate(value),
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
fooForm.handleSubmit();
}}
>
<fooForm.AppField children={(field) => <field.TextField label="Foo" />} name="foo" />
</form>
);
};
const fooSchemaClient = v.object({
foo: v.string('client message'),
});

const fooSchemaServer = v.object({
foo: v.string('server message'),
});

export const doSomethingOnTheServer = createServerFn({ method: "POST" })
.validator(fooSchemaServer)
.handler(async ({ data: foo }) => {
return await doSomething(foo);
});

const Form = () => {
const updateFooMutation = useMutation({
mutationFn: async (value: v.InferOutput<typeof fooSchemaClient>) => {
doSomethingOnTheServer({ data: value });
},
onError: (error) => {
console.error("Do something with the error", error);
},
});

const fooForm = useAppForm({
defaultValues: {
foo: "bar",
},
validators: {
onChange: fooSchemaClient,
},
onSubmit: ({ value }) => updateFooMutation.mutate(value),
});

return (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
fooForm.handleSubmit();
}}
>
<fooForm.AppField children={(field) => <field.TextField label="Foo" />} name="foo" />
</form>
);
};
Is it possible to show the validation error messages that come from the server?
absent-sapphire
absent-sapphire4w ago
the general channel has some discussion about this topic. Read from here https://discord.com/channels/719702312431386674/1100437019857014895/1415014824609452083

Did you find this page helpful?