Search code examples
sveltesveltekit

Sveltekit: Cannot call `fetch` eagerly during server side rendering with relative URL


I am struggling with an error trying to put together an infinite scroll table.

It works fine until I refresh the page and then I get the error "Cannot call fetch eagerly during server side rendering with relative URL (/api/clients/getClients) — put your fetch calls inside onMount or a load function instead"

I can't seem to find any documentation that clarifies what it means in this context and what could make a difference with reloading the page?

<script lang="ts">
    import {onMount, onDestroy} from "svelte";  

    import { goto } from '$app/navigation';
    import { page } from '$app/stores';
    
    import Table from '$lib/component/table/Table.svelte';



    // Data
    $:client_type = $page.params.type;
    let limit:number = 30;
    let offset:number = 0;
    $:search = "";

    let headers: any = [];
    let data:any = [];

    let newBatch:any = [];
    let noMore:boolean = false;
    
    async function fetchData() {
        
        const response = await fetch('/api/clients/getClients', {
            method: 'POST',
            headers: {'Content-Type': 'application/json',},
            body: JSON.stringify({ 'client_type': client_type, 'limit': limit, 'offset': offset, 'search': search })
        });
        let result = await response.json(); 
        headers = result.headers;
        newBatch = result.content;

        newBatch.length < limit ? noMore = true : noMore = false;
    };

    onMount(()=> {
        fetchData();
    })

    $: data = [
        ...data,
        ...newBatch
    ];    

    $: onChange(client_type, search);
    function onChange(client_type:string, search:string) {
        data = [];
        newBatch= [];
        offset = 0;
        noMore = false;
        fetchData();
    }


    // Infinite Scroll
    $:y = 0;
    $:h = 0;
    function handleScroll() {
        if(y > (h)) {
            h = h*2;
            offset = offset+limit;
            if(!noMore) {
                fetchData();
            }
        }
    }  


    // Click on Row
    function handleClick(event:CustomEvent) {
        goto('/client/'+event.detail.id.data);
    }
</script>



<svelte:window bind:scrollY={y} bind:innerHeight={h} on:scroll={handleScroll} />

<main>
{#key client_type || search}
    <Table headers={headers} content={data} on:clickRow={handleClick} />
{/key}
</main>

Solution

  • When the web browser fetches the first page from the SvelteKit server (as it does when you reload the page in the web browser), the server will run your SPA on the server, obtained the rendered HTML code, and send that to the web browser, so the web browser quickly can obtain and render the HTML code. The web browser will then start running the SPA itself, and from thereon it will continue to run as an SPA on the client.

    In your case, your component immediately calls onChange(), which then in turn calls fetchData(), which then in turn calls fetch(). This is what the server complains about. You should instead use the load() function.