Remote Functions: form validation #14288
Replies: 19 comments 42 replies
-
I tried solving this in a pet project by having a base schema (shareable between server and client), then extending it for a client-specific schema and a server-specific schema. The server schema would, like you said, make database calls, while the client schema would send requests to the server (like checking if a username is taken before we submit the registration form). I wonder if there's some magic that can happen here where we add a query remote function inside a schema and that just becomes a function call on the server but a fetch in the browser. |
Beta Was this translation helpful? Give feedback.
-
This is exactly why Svelte keeps showing who’s boss in the market. While other frameworks drown developers in boilerplate, SvelteKit is pushing forward with native, typed, and integrated form validation that solves a real DX pain point cleanly. Turning FormData into typed objects, supporting arrays and nested properties out of the box, and returning structured issues and inputs is nothing short of a game changer. Once again, Svelte proves it’s one step ahead: simple on the surface, powerful underneath. |
Beta Was this translation helpful? Give feedback.
-
I don't think this is a dealbreaker for me in particular, but the success of superforms indicates that it's an important feature for many people. I don't think My question is: which is more valuable, the ability to put server-specific validations inside your schema, or the UX improvement of instant client validation? I think the clear answer is the latter. Server-side validation pros:
Cons:
Universal validation pros:
Cons:
|
Beta Was this translation helpful? Give feedback.
-
Maybe this could solve confidential data being returned in export const myForm = form(
z.object({ password: z.string() }),
() =>{},
{
protectedInputs: ['password']
}
); |
Beta Was this translation helpful? Give feedback.
-
I really want a type-safe const { form, name }: {
form: TForm,
name: FormInputName<TForm>
} = $props(); |
Beta Was this translation helpful? Give feedback.
-
I do think there needs to be a way to list just the root-level issues somewhere, if I'm already displaying the field-level issues next to each field. |
Beta Was this translation helpful? Give feedback.
-
I'm using this small lib for this: https://github.com/fabian-hiller/decode-formdata. Maybe it can help as inspiration (or just use it directly) |
Beta Was this translation helpful? Give feedback.
-
This would be awesome! This is one of the best things about Angular that is is unique: Angular Forms. However, instead of reinventing validation, you allow Zod, Valibot, etc. So, my 2c would be for this to work both in Server Actions and in vanilla Svelte. J |
Beta Was this translation helpful? Give feedback.
-
Would love to see both server and client side validation!! |
Beta Was this translation helpful? Give feedback.
-
I hold to my core sincerity for the enormous amount of effort given to svelte — pushing the boundaries is what keeps this framework evolving. 👏 Let's 👏 go! My ultimate fear is violation of the principles Svelte promises: the values that have defined its identity and trust with the community. These include (some are verbatim):
The loser in this equation could be measured in the impact compromise has overall, in the long run. That said, baking form validation directly into Once SvelteKit ships with a parsing + Standard Schema pipeline (or similar), it’s reasonable for developers to assume:
That’s a large, ongoing surface area for a framework built on small, predictable primitives and an ethos of:
The proposal cuts across those lines by:
If we must, an idiomatic path forward could be that we keep This would preserve SvelteKit’s "small core, powerful primitives" DNA, avoids inheriting every parsing/validation edge case, addresses the assumptions problem, and still makes the happy path easy for those who opt in. ✌️ |
Beta Was this translation helpful? Give feedback.
-
Overall, looks good! Opt outTo opt out, I assume if you omit the schema argument, the callback you passed to Client-side validationNo client-side validation feels like a very bad downside. I get that some people want to wrap database calls inside their validation (which I think conflates two different things), but being able to do basic data validation in the form while the user is filling it out is what I would prefer. It's much nicer to be told about an error immediately as opposed to filling out the entire form, scrolling down, hitting submit, and then seeing errors. The manual |
Beta Was this translation helpful? Give feedback.
-
What would the api look like for populating a form with data? would inputs just be a <script lang="ts">
updateUser.inputs = await getCurrentUser();
</script>
<form {...updateUser}
<label>
Username:
<input name="username" bind:value={updateUser.inputs?.username} />
</label>
...
</form> |
Beta Was this translation helpful? Give feedback.
-
I was the person who set off the thread this discussion spun out from. I'm stuck using a third-party library to do client-side validation. As much as I'd like to rely on server-only validation, designers request experiences that require client-side validation. It's a deal breaker not to have client-side validation, and it was the impetus for my original comment. I need to be able to revalidate input, on input, and I need to manipulate the UI (button states, etc.) based on the validity of the form. |
Beta Was this translation helpful? Give feedback.
-
Not a dealbreaker if it's server-side only. In fact, feeling very guilty to have this much good DX. |
Beta Was this translation helpful? Give feedback.
-
Hi there! I'm the maintainer of formgator, a
|
Beta Was this translation helpful? Give feedback.
-
I've updated the original post with some designs around reactivity and client-side validation. You can read them up top, but tl;dr
|
Beta Was this translation helpful? Give feedback.
-
Seems complex to read :-), as I don't have immediate needs, it's just distracting me, because it is interesting. then every child comprennent would have a mapping property path to put the value in once hydrated client side. For example . Path is then used to construct the object out to read it to prefill the form. This allows the validation to take place client and server side with the same code, and allows two way bindings |
Beta Was this translation helpful? Give feedback.
-
We will still be able to use |
Beta Was this translation helpful? Give feedback.
-
This package enables client side validation for remote functions. It's interesting what people already come up with. Maybe this can inform some decisions for the official implementation. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
If you're passing arguments to
query
andcommand
remote functions, you're required to provide a Standard Schema for validation, but no validation is required for aform
.This caused many of you — justifiably — to raise eyebrows. The rationale was that the type of the argument is always going to be a
FormData
object, and you can't type the contents of aFormData
object (it's not generic), so you're going to have to do the validation inside the callback anyway. But it's clearly not ideal.Following on from a lengthy conversation on the original discussion, this is a proposal to make form validation built-in.
Conversion
The first step is to convert the data to an object that can be validated. In the basic case...
...this is easy — just create a
{ username: string, password: string }
object. What makes it a little trickier is that fields can be repeated......in which case you need to call
data.getAll('modifications')
on the resultingFormData
object. The wrinkle is that it's not possible to know, from the server's perspective, whethermodifications
is supposed to be astring
or astring[]
in the case that there's only one checked value.We can side-step that problem by disallowing duplicate fields and using syntax to denote arrays:
On the server, we can 'hydrate' these into arrays:
form.issues
andform.input
Armed with this, we can validate your data and report issues back to the client, along with the submitted data, in a structured way:
You could of course build opinionated abstractions around this:
Reactivity
myForm.input
would be updated oninput
events, allowing you to express relationships between form controls:Types
issues
is aRecord<string, Issue[]>
whereIssue
comes from Standard Schema. Each issue has amessage
and apath
property.input
is aRecord<string, FormDataEntryValue>
. (Not 100% sure yet what would happen in the case of an<input type="file">
.)Rolling up issues
Issues belonging to members of arrays are rolled up to the arrays themselves — in other words, to take our earlier food ordering example,
issues.modifications
would include any issues belonging tomodifications[0]
, etc. Theissue.path
can be used to distinguish between them.issues.$
could be a place to put root-level issues, and a rollup of all the issues found while validating.Manual validation
Since validators can be async functions, and since you can use
getRequestEvent
inside those functions, it should be possible to do the bulk of your checking inside your schema. As a bonus, if you have multiple async checks they will happen in parallel.There are some cases where you don't know if data is valid until you attempt to perform some action. In these cases, you might still want to report a validation error (populating
issues
andinput
) rather than returning or erroring. For that reason, we will probably need to provide a method for populatingissues
programmatically:invalid
would be typed such that you could only use keys matching the schema. It throws an understood-by-SvelteKit error, so as not to pollute the type of the return value — in other words, returning always indicates success. A non-invalid
error would cause the error page to appear, and thus should be avoided.Client-side validation
Validation is server-first, but you can add client-side preflight validation, and you can trigger validation programmatically to populate
issues
before the form is submitted.Preflight validation, like server validation, accepts a Standard Schema:
Now, before the form is submitted to the server, it will be checked against the preflight schema. If it fails to validate,
login.issues
will be populated. If it does validate, it will then be submitted so that you can validate the data on the server. This may involve the same schema (exported from a shared module, since you cannot export a schema from a.remote.ts
file — you can only export remote functions), but in many cases the server-side validation will include additional checks.Separately, you can programmatically validate the form on demand with the
validate
method. You can call this whenever you like, for example after an input is blurred:This will validate the data and populate
login.issues
, but it won't submit. By default, issues relating to controls that aren't yet dirty (i.e., they haven't been interacted with) would be omitted.Wrinkles
login.validate
? That would allow us to give immediate feedback, but it would also prevent server validation from occurring if the form was incomplete — for example in a registration form we wouldn't be able to check ifusername
was already in use untilpassword
had already been filled out.preflight
andvalidate
APIs to work, we need a 1:1 correspondence between<form>
and form function. (<form>
follows function, heh.) This is easy enough to implement with an attachment, and if you need multiple instances you can useform.for
(which we need to get around to documenting), but are there situations in which this would be restrictive, perhaps aroundbuttonProps
?Security
So far this proposal assumes that it's safe to return submitted data back to the user in case of a validation error. This gets slightly murky around things like passwords and credit card info.
The conventional wisdom on this seems to be that it is safe — if you trust everything between the browser and the server (which you have to in order to do anything) then it stands to reason that you can trust everything between the server and the browser.
But it deserves careful scrutiny. Does having your password in the HTML of the response (even if in a JSON object rather than the markup) create vulnerabilities that aren't already present? (Obviously if a bad actor has access to your computer, they can get your password from the HTML, but they could already have installed a keylogger or inspected
input.value
, so that's not a new vulnerability.) Given that POST requests aren't cached, I would like to imagine it doesn't. I hope that the AI companies currently threatening us with new browsers, so that they can crawl more content without having to circumvent anti-bot measures, are careful enough not to add POST responses to their training data.It's probably fine, but I'd love for people to share their thoughts on this!
Beta Was this translation helpful? Give feedback.
All reactions