Skip to content
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

SSR fetch without "res.json()" makes client call endpoint again #842

Closed
valterkraemer opened this issue Apr 2, 2021 · 6 comments
Closed
Labels
bug Something isn't working
Milestone

Comments

@valterkraemer
Copy link

Describe the bug

If not calling res.json() in the load() function, the svelte-data-script is not added during SSR, so the client will also do the same fetch.

Logs

--

To Reproduce

<script context="module">
  export async function load({ fetch }) {
    const res = await fetch('/ping.json');

    res.json(); // When commenting out this line, client will also fetch

    return {};
  }
</script>

<h1>Ping</h1>

Expected behavior

The client wouldn't do the fetch again

Information about your SvelteKit Installation:

Diagnostics
System:
    OS: macOS 11.2.3
    CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
    Memory: 537.48 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.5.0 - ~/.nvm/versions/node/v14.5.0/bin/node
    Yarn: 1.22.5 - ~/.nvm/versions/node/v14.5.0/bin/yarn
    npm: 6.14.5 - ~/.nvm/versions/node/v14.5.0/bin/npm
  Browsers:
    Brave Browser: 89.1.21.77
    Firefox: 86.0
    Safari: 14.0.3
  npmPackages:
    @sveltejs/kit: ^1.0.0-next.69 => 1.0.0-next.69 
    svelte: ^3.37.0 => 3.37.0 
    vite: ^2.1.5 => 2.1.5
  • Your browser: Safari

  • Your adapter: Node

Severity

Low, easily worked around.

@Rich-Harris
Copy link
Member

Curious about what the behaviour should be here? The assumption was that if you don't consume the body with res.text() or res.json(), it's because you're streaming the response or using res.arrayBuffer() — either way we don't want to serialize it. What's a scenario in which you want the response to be inlined but don't want to use text or json?

@valterkraemer
Copy link
Author

So I had this issue when wanting to set a cookie using fetch but didn't care about the response. (Setting cookie with fetch during SSR doesn't work btw, that depending on how you look at it also could be considered an issue).

I'm not going to use this approach, but thought that it was worth reporting anyway.

What's a scenario in which you want the response to be inlined but don't want to use text or json?

I would say that the question wasn't if I wanted the response inlined, I just didn't want to do the request twice. Once during SSR, and then again by the client.

A scenario where you don't care about the response could also be analytics, send some data like page viewed, but not caring about the response.

@Egnus
Copy link

Egnus commented May 19, 2021

Another side effect of this, depending on wether you transform the response or not (with res.json(), or whatever)
The responses from both client and server are completelly different in structure.
for instance:

// load() at /src/routes/xx/[slug]/index.svelte
const res = await fetch(`/xx/${page.params.slug}.json`);
if (res.redirected) { // We are not allowed
  console.log('should be redirected'); // The console is triggered in browser but not in SSR
  return {
    status: 303,
    redirect: '/no-rights' // This has no effect for not matching SSR and browser returned objects
  };
}

If the api response was:

return {
  status: 303,
  headers: {
    location: '/no-rights'
  }
}

The server gets as the response:

{
    url: undefined,
    status: 303,
    statusText: '',
    headers: { location: '/no-rights' },
    counter: undefined,
    highWaterMark: undefined
}

The browser gets:

{
  body: ReadableStream
  bodyUsed: false
  headers: Headers // This do not contain location
  ok: true
  redirected: true
  status: 200
  statusText: "OK"
  type: "basic"
  url: "http://localhost:3000/no-rights"
}

Things gets even more messy if I use res.text() for instance:
1 . If the page was harcoded the url and the app was loaded in that page. Both Browser and SSR gets the same SSR fetch response.
2. If i reach to this URL by using the svelte router. SSR will NOT fetch and the browser will gets only the browser response.

Because of this and how different the params in the response are, to cover all the cases I can only do this:

const res = await fetch(`/xx/${page.params.slug}.json`);
res.text(); // with this, first page is directly redirected, then back routes to the same path will make the screen blink as browsers fetch again
// without .text() page always blinks to fetch from browser.

// handler of load for all the cases
// left side ssr mode - right side browser mode
if (!res.ok && res.headers.has('location') || res.redirected) {
      return {
        status: 303,
        redirect: res.headers.get('location') || res.url  // ssr vs browser
      };
    }

The result of this is not nice as the route is not really protected and in some cases the browser still needs to load this again and the delay makes the screen to blink and expose the page momentanealy.

@yulduck
Copy link

yulduck commented Aug 20, 2022

Hi, any plan for fixing this issue?

@odama626
Copy link

I think I'm running into this issue as well, trying to do a client side fetch with .blob()

@dummdidumm
Copy link
Member

Closing as duplicate of #8302 - we will document this behavior, but in short there's nothing we can do about this here. We need some hook to serialize the response.

@dummdidumm dummdidumm closed this as not planned Won't fix, can't repro, duplicate, stale Feb 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants