Remote Functions: Queries require explicit call in a method for Single Flight mutations to work in .svelte.ts/js files
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 20.8k
- Forks
- 2.3k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 156
Description
Describe the bug
If, instead of calling remote queries directly in a component, I'd like to centralize them in a shared .svelte.ts/js file (so I can reuse the result of a remote query across components, initialize them in the parent, etc.), I have to explicitly call remote function (e.g. getPosts() ) in some method for a single flight mutations to work, even though I am already calling them in a constructor (inside of $derived()):
import { getPosts } from "./posts.remote.js";
class PostsStore {
// Ideally, calling remote function here should be enough
// and single-flight mutations should work
postQuery = $derived(getPosts());
posts = $derived.by(() => {
if (!this.postQuery.current) return [];
return this.postQuery.current;
});
initialized = $state(false);
loading = $derived(this.postQuery.loading);
error = $derived(this.postQuery.error);
load = () => {
if (this.initialized) return;
this.initialized = true;
// Comment this out and observe that
// posts are not refreshed when you create a new post
this._initRemoteFunctions();
};
// We have to explicitly call this in order for single-flight mutations to work
private _initRemoteFunctions = () => {
getPosts();
// I'll likely put other queries here a well if this won't be fixed
// getCategories();
// getComments();
};
}
export const postsStore = new PostsStore();
Remote functions
import { form, query } from "$app/server";
import { error } from "@sveltejs/kit";
import { MockDB } from "./DB.js";
import { CreatePostFormSchema } from "./schemas.js";
const db = new MockDB();
export const getPosts = query(async () => {
return await db.getPosts();
});
export const createPost = form(CreatePostFormSchema, async ({ content }) => {
const newPost = await db.createPost(content);
// This only works when we explicitly call getPosts()
// In some method of the store
await getPosts().refresh();
});
Layout where I init the store:
<script lang="ts">
import { postsStore } from '$lib/posts.svelte.js';
import { onMount } from 'svelte';
let { children } = $props();
onMount(() => {
postsStore.load();
});
</script>
{@render children?.()}
Feed
<script lang="ts">
import { postsStore } from './posts.svelte.js';
</script>
{#each postsStore.posts as post (post.id)}
{post.content}
{/each}
It it still wonderful that I am able to do this in the first place (even though with a "workaround").
Reporting as I think either I am doing something wrong or this is worth considering for you in the development of remote functions, as using remote functiosn in custom stores is amazing :)
Reproduction
Reproduction in stackblitz:
https://stackblitz.com/edit/sveltejs-kit-template-default-gghvhpik?file=src%2Flib%2Fposts.svelte.ts
Repo itself:
https://github.com/ViaAnthroposBenevolentia/sveltekit-remote-fn-reproduction
Logs
System Info
System:
OS: macOS 15.5
CPU: (8) arm64 Apple M3
Memory: 237.19 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.16.0 - ~/.nvm/versions/node/v22.16.0/bin/node
Yarn: 1.22.22 - /opt/homebrew/bin/yarn
npm: 10.9.2 - ~/.nvm/versions/node/v22.16.0/bin/npm
pnpm: 10.16.1 - /opt/homebrew/bin/pnpm
Watchman: 2025.09.08.00 - /opt/homebrew/bin/watchman
Browsers:
Chrome: 140.0.7339.210
Safari: 18.5
npmPackages:
@sveltejs/adapter-auto: ^6.0.0 => 6.1.0
@sveltejs/kit: 2.43.5 => 2.43.5
@sveltejs/vite-plugin-svelte: ^6.0.0 => 6.2.1
svelte: 5.39.6 => 5.39.6
vite: ^7.0.4 => 7.1.7
Severity
annoyance
Additional Information
My current set up and this 'workaround' is still not bad, but just to let you know :)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked StackBlitz reproduction and src/lib/posts.svelte.ts, then compare the constructor’s $derived(getPosts()) with the explicit getPosts() call in _initRemoteFunctions(). Exercise createPost and observe whether the shared store refreshes after the mutation. Done means the constructor-only setup refreshes posts without the extra call.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100