Skip to content

Remote Functions #13986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 153 commits into from
Jul 31, 2025
Merged

Remote Functions #13986

merged 153 commits into from
Jul 31, 2025

Conversation

dummdidumm
Copy link
Member

@dummdidumm dummdidumm commented Jul 10, 2025

PR for #13897, discussion should happen there.

For a more complete commit history (with lots of explorations and dead ends; also includes some caching ideas) see #13957


Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

* initial commit

* various fixes

* tweak demo

* fix

* remove acorn-typescript

* simplify

* fix

* generate, don't transform

* use an x- header

* regenerate manifest when remote files are added/removed

* move RPC logic out of page, it belongs elsewhere

* use _app/remote/x

* tighten up error handling

* unused

* CSRF

* smaller payloads

* belt and braces

* don't use 204, that only applies to DELETE/PUT requests

* turn remote_call into a factory - more compact and gives us a lot more options

* analyze exports of remotes dynamically, add query/action/formAction as different types of remotes

* update playground

* POC: hydrate query results

* remote form actions that hydrate and work without JS

* conditional

* load fn WIP

* rerun query on invalidation

* make it possible to call invalidate in rpc functions

* fix

* adjust form API

* fix dev stale bug

* let rpc partake in prerendering

* prerender POC

* cache POC

* simplify server remote logic by leveraging ESM self imports

* cleanup server remote info

* rename functions + some docs

* move more stuff into functions to deduplicate/cleanup/make connections clearer

* prerendering

* hide remote url, avoid unnecessary work

* cache refinement

* various fixes

* tests

* don't call prerender function at runtime, tweak tests

* tweak

* tweak caching behavior

* remove cache function from public API for now

* add experimental flag

* remove load helper function for now

* move file

* add refreshAll, related to #13139

* adjust overrides signature

* query/redirect/form-fail handling

* adjust caching behavior to cache until query is removed

* disallow non-remote-function exports from .remote files

* bump dts-buddy to be able to generate types without bugs again

* handle query redirect without going through error boundaries

* harmonize refresh with override signature

* fixes

* resolve cyclic import dependency

* prerender treeshaking

* refine API

* refresh from the server

* adjust tests, fix

* adjust prerender

* reorder functions

* make query eager on the client if in reactive context

* implement current/error/pending

* remove optimistic in favor of override callback

* add validate

* form.for(...)

* tweak api around redirects; allow single flight mutation redirect

* implement stacking override API

* fixes

* deduplicate remote calls on the server during full page visits

* rework to use resource class, fix some bugs

* ensure refresh/then resolve in order

* microtask tweaking

* fix

* cleanse event for remote functions

* implement `updates` and `withOverride` for command

* implement `updates` and `withOverride` for form

* cleanup

* Prevent state_unsafe_mutation error

* restrict to 0 or 1 argument, enforce validation

* validation tests

* tweak

* comment out for the time being

* remove validate function

* lint

* guard

* bump Svelte version

* silence type errors

* Update packages/kit/src/runtime/app/server/remote.js

Co-authored-by: Paolo Ricciuti <[email protected]>

* fix

* update playground

---------

Co-authored-by: Dominic Gannaway <[email protected]>
Co-authored-by: Rich Harris <[email protected]>
Co-authored-by: Paolo Ricciuti <[email protected]>
Copy link

changeset-bot bot commented Jul 10, 2025

⚠️ No Changeset found

Latest commit: 4706f36

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@sveltejs sveltejs locked as too heated and limited conversation to collaborators Jul 10, 2025
Copy link
Member

@Rich-Harris Rich-Harris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's going to take a few passes to fully review this glorious beast, but here's a few notes to start with. also:

  • will need a changeset 😆
  • if you have a .remote.ts that imports query or whatever from $app/server, then at build time you'll get a 'Cannot import $app/server into client-side code' error rather than the more helpful remote-functions-specific error. Would be nice to be more specific here while not creating a breaking change for anyone that happens to have an existing .remote.ts file (in which case they would probably appreciate a warning that they'll definitely need to rename that file)

Also, I'll probably find the answer to this as I read on but didn't want to lose the thought: are we (de)serializing arguments to remote functions that are called directly on the server? On the one hand it's wasteful, on the other it would avoid a client/server discrepancy (e.g. a mutation being respected on the server but not on the client)

Absolutely pumped for this to land

@Rich-Harris
Copy link
Member

Gonna update the behaviour of forms around errors, because it's slightly inconsistent at present. On a form that doesn't use .enhance(...), if a submission errors then the nearest +error.svelte component is rendered. On a form that does use .enhance(...), the nearest +error.svelte page is rendered if JavaScript isn't available, otherwise myForm.error is populated, regardless of whatever error handling logic exists within the enhance callback (which is slightly weird in the case where you try-catch and either handle it or throw a different error).

Aside from the inconsistency, the most likely outcome of this is that errors will just end up getting swallowed, as developers forget to add {#if myForm.error} UI. I'm therefore going to change it so that the nearest +error.svelte is always rendered if there's an unhandled error.

If you try-catch the submit() call, you can (and should, since showing the error page is the second worst outcome behind a silent failure) handle the error however you choose in the progressively enhanced case.

@Rich-Harris
Copy link
Member

Am second-guessing the my_form.for(key) API — I'm having a rather hard time coming up with a good/non-contrived example of what to use it for. (The current example has 'something went wrong' suggesting a 5xx error, but in that case surely you wouldn't return at all?)

For validation errors it feels like you could rely on client-side validation (including things like minlength) for the vast majority of things. AFAICT you only really need an independent result property when you need to show validation errors/previously-submitted data, can't use client-side validation to prevent the bad submission in the first place, and need per-instance UI. I'm sure those cases do exist but I'm having such a struggle thinking of a realistic one that I'm just going to punt on it for now and remove that section of the docs

@Rich-Harris
Copy link
Member

I think we need to come up with a better name than formAction. It's weird for that object to contain a formAction property, and also weird that something called formAction contains a bunch of other properties. It also feels a bit weird to pick the camelCased property name over the lowercased attribute name, when the attribute is the thing that gives forms their magical progressive enhancement powers.

Looking at MDN, the only obvious alternative is override, though that's perhaps a bit overloaded.

(I'm tempted to suggest just throwing formaction onto the form object itself, alongside action, so that you can use it on a <form> or a <button>. That might offend purists though.)

Also we should probably have formmethod on there as well

@dummdidumm
Copy link
Member Author

dummdidumm commented Jul 31, 2025

AFAICT you only really need an independent result property when you need to show validation errors/previously-submitted data, can't use client-side validation to prevent the bad submission in the first place, and need per-instance UI

In other words you already know when it's valuable - so we shouldn't remove it. One example are validation errors that happen on the server, like "you are not allowed to delete this". Another is to disable the button while a submission is in progress (which we need to add anyway AFAICT).

Related: #7175

I'm tempted to suggest just throwing formaction onto the form object itself

Sounds tempting indeed. I fear that the onclick handler intercepting unexpectedly could throw people off, also it would probably confuse people why there are more attributes than necessary on the form.

When coming up with an alternative name I would then choose something that is not an attribute name, that somehow brings across the intent. secondaryAction or something like that.

@Rich-Harris
Copy link
Member

Yeah, not suggesting removing for, just the docs for for until we can come up with a good example.

Consensus on naming appears to be formAction -> buttonProps, will make that change

@dummdidumm
Copy link
Member Author

Re example: what about pending UI which is made possible through a new Boolean (which we should have anyway) form.submitting?

@Rich-Harris
Copy link
Member

Ooh that's a good idea. In the interests of shipping I think it should probably be a follow-up PR but yeah I like that

Copy link
Member

@Rich-Harris Rich-Harris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incredible work. lots more stuff to add in follow-up PRs but this is such a good foundation for everything to come

@Rich-Harris Rich-Harris merged commit 793ae28 into main Jul 31, 2025
21 checks passed
@Rich-Harris Rich-Harris deleted the remote-functions branch July 31, 2025 20:41
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants