HoudiniGraphql / houdini

The disappearing GraphQL framework
http://www.houdinigraphql.com
MIT License
913 stars 99 forks source link

SvelteKit: Fragment stores become empty after hydration when using +page.server.ts server-side loading alongside +layout.gql #1314

Closed nermolov closed 3 months ago

nermolov commented 4 months ago

Describe the bug

On one page in my SvelteKit application, I need to conditionally call a private API based on the response of my GraphQL query, so I switched from using a +page.gql query to a manual server only load as described in the documentation. I still have a standard client/server query in my global +layout.gql file. It seems that the combination of these two causes my page-level fragment stores to get cleared out after initial hydration, despite the fact that all of the data is still there.

page.gql:

query AnimeQuery {
  Media(isAdult: false) {
    ...AnimeTitleFragment
  }
}

+page.server.ts:

import { AnimeQueryStore } from '$houdini';
import type { PageServerLoad } from './$houdini';

export const load: PageServerLoad = async (event) => {
  const myQuery = new AnimeQueryStore();
  const { data } = await myQuery.fetch({ event });

  return { data };
};

+page.svelte:

<script lang="ts">
  import AnimeTitle from '$lib/AnimeTitle.svelte';
  import type { PageData } from './$houdini';

  export let data: PageData;
</script>

<AnimeTitle animeTitle={data?.data?.Media} />

<h3>Contents of `data`</h3>
<pre>{JSON.stringify(data, null, 2)}</pre>

AnimeTitle.svelte:

<script lang="ts">
  import { fragment, graphql, type AnimeTitleFragment } from '$houdini';

  export let animeTitle: AnimeTitleFragment | null | undefined;
  $: animeTitleFragment = fragment(
    animeTitle,
    graphql`
      fragment AnimeTitleFragment on Media {
        title {
          romaji
          english
          native
        }
      }
    `
  );
</script>

<h1>{$animeTitleFragment?.title?.english}</h1>

+layout.gql:

query LayoutQuery {
  Character(search: "Spike") {
    name {
      full
    }
  }
}

+layout.svelte:

<script lang="ts">
  import type { LayoutData } from './$houdini';

  export let data: LayoutData;
  $: ({ LayoutQuery } = data);
</script>

<p>{$LayoutQuery?.data?.Character?.name?.full}</p>
<slot />

SSR page/with javascript disabled/before hydration: image

Page with javascript enabled/after hydration: image

The above code is included in the linked minimal reproduction repository.

Reproduction

https://github.com/nermolov/sveltekit-houdini-server-load

AlecAivazis commented 4 months ago

Huh that's strange. Sorry for lagging on the reply here and thanks for taking the time to put together a reproduction. I'll try to find some time to look into it in the next few days

AlecAivazis commented 3 months ago

Okay, I finally found the time and have a good idea of what's going on. Since you are loading your graphql query on the server, the client-side cache does not access to that data and so when the app hydrates and tries to hook up to the cache, it doesn't have anything.

I'm not quite sure what the correct work around is for this just yet but I will keep thinking on it. Until then, the only real option is to either not load the data on a server file, or to not use the fragment.

AlecAivazis commented 3 months ago

i'm going to close this in favor of #675

nermolov commented 3 months ago

@AlecAivazis is there a way to manually inject data into the client side cache?

nermolov commented 3 months ago

I've decided to use the following workaround in all of my fragment components for this route https://github.com/nermolov/sveltekit-houdini-server-load/commit/b39633ac8b85032184213c38235c2e8a07d9c5c5 seems to work fine given that all of the relevant data is only loaded once for this route/there are no mutations.