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

Cannot use relative URL with global fetch in .svelte file #8536

Closed
kegesch opened this issue Jan 15, 2023 · 17 comments · Fixed by #8551
Closed

Cannot use relative URL with global fetch in .svelte file #8536

kegesch opened this issue Jan 15, 2023 · 17 comments · Fixed by #8551

Comments

@kegesch
Copy link

kegesch commented Jan 15, 2023

Describe the bug

According to the documentation, I should be able to create an endpoint in a +server.js file and call that endpoint with a relative url via fetch from +page.svelte (https://kit.svelte.dev/docs/routing#server-receiving-data).

This is not possible anymore. I get an error message that was introduced with #8370 :

Cannot use relative URL (<relative-url>) with global fetch — use `event.fetch` instead: https://kit.svelte.dev/docs/web-standards#fetch-apis

Hower, the referenced documentation does not even mention event.fetch and also does not explain why this is not possible in .svelte files.

Reproduction

The example of https://kit.svelte.dev/docs/routing#server-receiving-data .

Logs

Server Log:

Error: Cannot use relative URL (/search?query=) with global fetch — use `event.fetch` instead: https://kit.svelte.dev/docs/web-standards#fetch-apis
    at globalThis.fetch (file:///<path>/node_modules/@sveltejs/kit/src/exports/vite/dev/index.js:35:10)
    at getSearchResult (/src/routes/search/+layout.svelte:12:26)
    at Timeout.eval [as _onTimeout] (/src/routes/search/+layout.svelte:30:11)
    at listOnTimeout (node:internal/timers:564:17)
    at process.processTimers (node:internal/timers:507:7)


### System Info

```Shell
OS: Windows 10 10.0.22621
    CPU: (12) x64 AMD Ryzen 5 5600X 6-Core Processor
    Memory: 8.86 GB / 31.93 GB
  Binaries:
    Node: 18.12.1 - C:\Program Files\nodejs\node.EXE
    npm: 8.19.2 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.22621.1105.0), Chromium (108.0.1462.76)
    Internet Explorer: 11.0.22621.1

Severity

blocking an upgrade

Additional Information

No response

@dummdidumm
Copy link
Member

dummdidumm commented Jan 16, 2023

This is not reproducible given the code snippet. Looking at your error log, it sounds like you eagerly use fetch on the server inside your Svelte file, which probably is not what you want since it's not waited on until the response is rendered.

That said, the error is confusing since it's not called inside a load function.

Could you please provide the code of your +layout.svelte so we can see that code that's actually causing this? Are you sure the code in question worked prior to that change? I think it would have emitted a different error, but still an error.

@amr3k
Copy link
Contributor

amr3k commented Jan 16, 2023

I'm facing this issue as well,and I don't use fetch anywhere server-side.
I had to replace all relative URLs :(
image

@dummdidumm
Copy link
Member

Could one of you please provide a minimum reproducible with code that worked before this change and now no longer doesn't? I'm having a hard time seing how this worked before; I thought that there would be a "invalid URL" failure then.

@adriandersen
Copy link

Could one of you please provide a minimum reproducible with code that worked before this change and now no longer doesn't? I'm having a hard time seing how this worked before; I thought that there would be a "invalid URL" failure then.

Not sure what change you are refering to, but if relative URLs is not supposed to work, does that mean the documentation refered to in the original post should be changed? There seems to be a mismatch.

@kegesch
Copy link
Author

kegesch commented Jan 16, 2023

I can not, i justed with svelte yesterday and assumed, from reading the docs, that this should work, since this is basically the example of: https://kit.svelte.dev/docs/routing#server-receiving-data

Nonetheless, here is my +layout.svelte:

<script lang="ts">
    let searchTerm = ""
    let results = []

    async function getSearchResult() {
        const response = await fetch(`/search?query=${searchTerm}`, {
          method: 'GET',
          headers: {
            'content-type': 'application/json',
            'accept': 'application/json'
          }
        });

        results = await response.json();
      }

    let timer
    async function debounce(func) {
		clearTimeout(timer);
		timer = setTimeout(async () => {
			await func();
		}, 750);
	}

    $: {
        debounce(getSearchResult)
    }


</script>

    <div class="md:container md:mx-auto flex py-3 gap-2">
        <img src="logo.png" alt="logo" class="h-10" />
        <input class="flex-1 bg-slate-500 h-10 rounded-lg drop-shadow-md p-2 text-slate-800" type="text" bind:value={searchTerm} placeholder="Search for a software"/>
    </div>

    <div class="md:container md:mx-auto flex py-3 gap-2">
        <div class="text-medium text-2xl text-slate-200">
            {#if searchTerm !== ""}
            Results for  '{searchTerm}'
            {/if}

            {#each results as d}
                <p>{d}</p>
            {/each}
        </div>
    </div>
    <div><slot/></div>

</div>

and my +server.ts:

/** @type {import('./$types').RequestHandler} */
export async function GET({ request }) {
    return json({})
}

@adriandersen
Copy link

adriandersen commented Jan 16, 2023

Can confirm i get the same error inside a load-function as well

@dummdidumm
Copy link
Member

@adriandersen please provide a code snippet of the load function

@dummdidumm
Copy link
Member

@kegesch judging from the code snippet this could never have worked, you would have gotten a different error (which is generic and therefore we added a more descriptive error in #8370), but you would have gotten an error.

@amr3k your case is interesting. Using {#await ..} like that on the server does swallow the error, which is why it worked for you before - but probably not the way you expected it. On the server, {#await ..} is not actually awaited, instead you fire off a request which is ignored (or in your case, previously errored because the URL is invalid).

Both of your use cases sound like you maybe are not interested in server side rendering at all? In this case you can turn your SvelteKit app into an SPA by adding export const ssr = false to a root +layout.js file.

@kegesch
Copy link
Author

kegesch commented Jan 17, 2023

Okay, but just for the clarification. If I understood it correctly, the example in https://kit.svelte.dev/docs/routing#server-receiving-data is wrong. Rather, fetch with relative paths should be done in the load-method. And if I want to POST something via fetch from the client to the API, I have to pass the complete URL, not just the relative path?

@Zerdayne
Copy link

As I load the data initially with PageServerLoad (https://kit.svelte.dev/docs/load#page-data), I for my part do not need to do a fetch in a svelte component while it is rendered server side. But I do need to do a fetch later on, which will only be on client side. My solution was to wrap it with the browser environment variable from sveltekit.

import { browser } from "$app/environment";

if (browser) {
  const res = await fetch(
    // url
  );
  data = await res.json();
}

With this I did not get the error "Cannot use relative URL (...) with global fetch".

Just writing it as this may help some others reading this.

@ndrswlkr
Copy link

ndrswlkr commented Jan 24, 2023

I loaded a small set of items via page.js and then tried to load more on intersecting when i run in to this error.
edit: it worked well while just intersecting. but then i added a filter section (filter acts on +server.js)
it was working when klicking the navbar link, but not on page reload

import { browser } from "$app/environment";

if (browser) {
  const res = await fetch(
    // url
  );
  data = await res.json();
}

This was the solution for me as well, thank you Zerdayne

@amr3k
Copy link
Contributor

amr3k commented Jan 26, 2023

@dummdidumm Thanks, that explains a lot, but I don't want to disable ssr for seo reasons, and I don't want to keep using environment variables because the development branch build will break.
Is there another way to just suppress the error message and keep building as before?

@dummdidumm
Copy link
Member

The thing is running fetch eagerly on component render (which also happens if you do {#await fetch(..)} in your template) doesn't have the outcome you want. On the server, it will invoke the fetch but will never use that result. Possible fixes:

  • put those fetch calls inside a load function instead and use event.fetch from load
  • guard all eager fetch calls with the browser boolean to not make them on the server

@Rich-Harris Rich-Harris added this to the soon milestone Jan 28, 2023
Rich-Harris added a commit that referenced this issue Feb 9, 2023
* fix: adjust fetch error message on the server

closes #8536

* make it possible to still use them in #await blocks

* undo #await dance

* put additional dev time check inside render.js instead

* Create five-pumpkins-join.md

* prevent corrupted state

* Delete nice-mayflies-compete.md

---------

Co-authored-by: Rich Harris <richard.a.harris@gmail.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
@danielmarkow
Copy link

how to fetch in the load function is described here:
https://kit.svelte.dev/docs/load#making-fetch-requests

@jasong-au
Copy link

jasong-au commented Sep 26, 2023

I had this same problem and just didn't understand. It seems like the conversation was over my head. The error message says 'use event.fetch instead' and the linked page never references 'event'. The parameter passed to 'load()' is actually event, but all the examples I've seen use destructuring and I didn't know the parameter was called 'event', or that it had a 'fetch' property since I had only seen 'params' destructured (i.e. event.params). I believe this is what using 'event.fetch' means:

export async function load({ params, fetch }) { // <--- event.params and event.fetch
  fetch('/realative/url') // parameter hides normal fetch() function
}

@ahmadshiddiqn
Copy link

I had this same problem and just didn't understand. It seems like the conversation was over my head. The error message says 'use event.fetch instead' and the linked page never references 'event'. The parameter passed to 'load()' is actually event, but all the examples I've seen use destructuring and I didn't know the parameter was called 'event', or that it had a 'fetch' property since I had only seen 'params' destructured (i.e. event.params). I believe this is what using 'event.fetch' means:

export async function load({ params, fetch }) { // <--- event.params and event.fetch
  fetch('/realative/url') // parameter hides normal fetch() function
}

is this fixed? or is it on different issue since this issue is closed

@iInvisibilities
Copy link

Yes you forgot to import the fetch in the load function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.