Best practices for validating form data in an action?
Answered
Dwarf Crocodile posted this in #help-forum
Dwarf CrocodileOP
A server action gets a
FormData
as an input, which is a list of objects. Are there any best practices on how to validate it? And also to catch/handle the respective errors in the form?Answered by codekrafter
in
in the client component
actions.js
:'use server'
async function addItem(data) {
const cartId = cookies().get('cartId')?.value
const result = await saveToDb({ cartId, data })
return result
}
in the client component
'use client'
...
import { addItem } from './actions'
...
export default function AddToCart({ productId }) {
const [error,setError] = useState(null)
return (
<form onSubmit={(event) => addItem(...).then(r) => setError(r)}>
<button type="submit">Add to Cart</button>
<span class="error">{error}</span>
</form>
)
}
34 Replies
You may be able to use a library like this to use Zod for validation: https://www.npmjs.com/package/zod-form-data
Dwarf CrocodileOP
My question is though, suppose you validate the data and throw an error. How can you show the user feedback that there was an error? Assuming you did something like:
<form action={myAction} ...>
and within myAction
you throw an error if there's something wrong with the data, or authentication, etc. How do you bubble up that error properly to the interface? https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#validationSloth bear
If you use server actions from a client component, you can treat the server action as a promise, and chain a .then after it to catch the response.
There is also this twitter thread that may help: https://twitter.com/dan_abramov/status/1654336219919048704
Dwarf CrocodileOP
Thanks for the useful links. I think one thing that would make all this more clear is, how do you even handle a successful server action for a form submission? How can you pass data returned from the server action back to the form/component so it can display feedback? Not clear from the docs to me.
Sloth bear
It works the same as a promise. You can return data directly from the server action. In the form action attribute, you can use .then to catch that returned data and use it.
Dwarf CrocodileOP
@Sloth bear can you please direct me to an example using
.then
to catch the error? That is the specific part that I don't get.Or using
.then
to catch any returned data. That seems really powerful and would solve my problem. Thank you.Dwarf CrocodileOP
That page has zero instances of
.then
😕It has an await call which is functionally the same
Dwarf CrocodileOP
Ah and if I inline the action inside a client component, I could do stuff like
useState
, etc.?If you call the server action from a client component, you could update state by awaiting (or using .then) the result
Dwarf CrocodileOP
kind of like this?
export default function AddToCart({ productId }) {
async function addItem(data) {
'use server'
const cartId = cookies().get('cartId')?.value
const result = await saveToDb({ cartId, data })
if (result.errors) {
setError(result.errors[0])
}
}
return (
<form action={addItem}>
<button type="submit">Add to Cart</button>
<span class="error">{error}</span>
</form>
)
}
You can’t have the set error call in the server action, you need it in the function that calls the server action
Which would require manually calling it instead of using a form action
Dwarf CrocodileOP
How do you manually call it?
Like using
onClick
?I'll type up an example real quick
Dwarf CrocodileOP
Incredibly grateful.
in
in the client component
actions.js
:'use server'
async function addItem(data) {
const cartId = cookies().get('cartId')?.value
const result = await saveToDb({ cartId, data })
return result
}
in the client component
'use client'
...
import { addItem } from './actions'
...
export default function AddToCart({ productId }) {
const [error,setError] = useState(null)
return (
<form onSubmit={(event) => addItem(...).then(r) => setError(r)}>
<button type="submit">Add to Cart</button>
<span class="error">{error}</span>
</form>
)
}
Answer
Very rough example, just make sure to only return serializable data
Dwarf CrocodileOP
Really appreciate this @codekrafter ðŸ™
happy to help! I think the whole server actions area is still pretty in-progress and experimental so some quirks are still being smoothed out and I am sure this will be a lot cleaner in the future
Dwarf CrocodileOP
It is nice enough that I'm willing to give it a shot.
One last comment @codekrafter - the form data would be under
event.target.inputNameHere.value
? No way to just access FormData
in that case right?You can construct
FormData
from a form element: https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormDataDwarf CrocodileOP
pardon the ignorant question, how do I access the
FormData
from the example you sent?Should be something like
new FormData(event.target)
Dwarf CrocodileOP
🆗 thank you!
Satin
Hey there! So this means that you can't show any errors without JS? So if JS is disabled and there is an error you don't see the feedback
With that solution, yes. I am not sure if there is a way to display errors atm with progressive enhancement
Satin
Damn, thanks for the reply