sveltejs / sveltejs/kit

Remote Functions: Queries require explicit call in a method for Single Flight mutations to work in .svelte.ts/js files

Open
#14,559 0 comments 1 reaction 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.