T
TanStack5mo ago
multiple-amethyst

Server Side validation as part of submit: Handling 422 responses

I do not want to use client side validation: My server side mutation (tanstack query) does the validation and responds with a 422 error response which contains the validation errors. This pattern is necessary anyway, when you have validation issues, that might depend on complex database state, which can only be checked in the same transaction you would like to change and commit the data. However, in the docs I do not see a good way how to do that. So, as of now I do mutationQuery.mutate(payload...); And on the mutationQuery.onError I process the validation error. When I process the error, when it occurs, I set get the fieldInfo of the field having the error and use setFieldMeta form.setFieldMeta(actualFieldName, (meta) => { const error = fieldErrorByFieldNameFromLastError[fieldName]; if (!error) return meta; return { ...meta, errorMap: { ...meta.errorMap, onSubmit: error, }, }; });
Note, that I use errorMap.onSubmit. However, this behaves cumbersome. 1) Whatever I set here is not unpacked into field.errors list. It is only present in the errorMap of the field. 2) How can I set validation errors on the form itself, when I have validation errors, that are not mappable to a specific field? There is a form.setErrorMap on the form, but the structure is not really transparent to me. All this makes me assume, that probably this is altogether not the right way to do it.
11 Replies
harsh-harlequin
harsh-harlequin5mo ago
Sounds like you're looking for form level async validators that trigger on submit. You can read up on it here: https://tanstack.com/form/latest/docs/framework/react/guides/validation#validation-at-field-level-vs-at-form-level This allows you to set form level as well as field level. The chain would be the following: - Form validators is triggered (on submit async) - This triggers tanstack query. - The query parses the error and returns it proper. - The validator consumes the parsed error and transforms it into form/field level errors
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...
harsh-harlequin
harsh-harlequin5mo ago
perhaps I misunderstood your structure with tanstack query, so let me know if this isn't the answer you're looking for
multiple-amethyst
multiple-amethystOP5mo ago
Thank you for getting back to me! I might understand it wrong, but the approach you linked to, assumes, that I would have a separate validation endpoint, that I would use in the validators of the form. However, this is not the environment I'm having: The validations are relying on complex and fastly changing database state and can only run in the same transaction, that would do the mutation and the commit of the changed data in the database. To put it the other way around: When I would do the validation in one request and then the mutation in a subsequent request, the related data might have already changed so that the mutation request would still fail with a 422. So, I still have the need to unwrap the 422 validation errors after the onSubmit of the form and not in a previous (asyncronous) validator request. Can I do that cleanly with Tanstack Form while getting rid of the issues described in my above post (errors not mappable to a single field, errors list on the field unwrapping the errorMap)?
harsh-harlequin
harsh-harlequin5mo ago
I see now! So your endpoint either successfully processes your submission or returns an error detailing issues with the submission? The code flow of form.handleSubmit() is as follows: * Check if canSubmit is false, return if it is * Run sync field validators, return if errored * Run async field validators, return if errored * Run sync form validators, return if errored * Run async form validators, return if errored * Run the provided onSubmit callback when creating the form With your structure, it may be worth skipping onSubmit and instead checking for the isSubmitSuccessful state. Since the async form validator would be run last, it would guarantee that going beyond it was a successful database transaction.
multiple-amethyst
multiple-amethystOP5mo ago
I see now! So your endpoint either successfully processes your submission or returns an error detailing issues with the submission?
Exactly Regarding your next, longer answer, do I understand that correctly, that supporting this use case would require a change in tanstack form? Or is there something I could do differently? Is the way I'm currently doing it supported at all or am I breaking the lifecycle of things, when I change the fields errorMap using setMeta of the fields when handling the error in mutationQuery.onError?
harsh-harlequin
harsh-harlequin5mo ago
you're sort of breaking the lifecycle, yes. You usally don't have to manually set error maps, it's expected that validators take care of that as mentioned above, the onSubmit callback is just the final function that it calls. validators can mutate data as well, the return type simply dictates whether the form considered the submission a failure or not TL;DR your use case seems fine, but your current solution is probably not worth the headache it causes
multiple-amethyst
multiple-amethystOP5mo ago
Ah, I think now I understand: Are you suggesting, to not use onSubmit to call the mutation query, but an async validator on the form? https://tanstack.com/form/latest/docs/framework/react/guides/validation#setting-field-level-errors-from-the-forms-validators Something like this maybe
const form = useForm({ defaultValues: { age: 0, }, validators: { onSubmitAsync: async ({ value }) => { try { const result = await mutationQuery.mutateAsync(value); } catch (error) { return { form: 'Invalid data', // The form key is optional fields: unwrapErrorsFromException(error), } } return undefined; }, }, })
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...
harsh-harlequin
harsh-harlequin5mo ago
yeah! looks about right a note btw, fields in the error response is not a nested object, but a record.
- fields: {
- person: {
- age: 'Invalid!'
- }
- }

+ fields: {
+ 'person.age': 'Invalid!'
+ }
- fields: {
- person: {
- age: 'Invalid!'
- }
- }

+ fields: {
+ 'person.age': 'Invalid!'
+ }
it's been a while since I wrote those manually, so hopefully I remembered it right
multiple-amethyst
multiple-amethystOP5mo ago
Cool, thank you very much! I will refactor right now!
variable-lime
variable-lime3mo ago
Is this really the right approach? i have a login form for example that i want to submit onSubmit, it either logs in or throws errors that the credentials are wrong. I want to set this error on a field that was thrown by the server. Doing this in validators.onSubmit feels really hacky. How could we not just set errors in the onSubmit function? This feels like this approach is totally overlooked what is a shame since almost every api works like this. Who does a server validation first before doing the actual action?
harsh-harlequin
harsh-harlequin3mo ago
your action and server validation is one and the same if no error returned, then your action automatically succeeded. You‘re asking for validation inside the successful section of the form yes, it can feel strange to put actions in validators, but your response from that action is a validation, not a general error if the only error returned is a 500 error, then you would be totally correct that it doesn‘t belong in onSubmit validation. That would belong in some external error that is unrelated to your form.

Did you find this page helpful?