kawa
kawa

Reputation: 611

How do I fetch a post by ID without API and stop calling the API every time I navigate back to it?

Stack

Setup

I have two different pages (HTML omitted):

  1. posts/+page.svelte
  2. post/[id]/[title]/+page.svelte

I use TanStack Query for storing data from the API. While I reference Svelte, I also use the documentation for React.js as most syntax seems to be compatible.

Here are the two major problems I am facing:

Here is the simplified code for the two files:

src/routes/posts/+page.svelte

<script lang="ts">
  import { onMount } from 'svelte';
  import { createQuery } from "@tanstack/svelte-query";
  import type { Post } from "$lib/models/Post";
  import { fetchPaginatedPosts } from "$lib/posts/fetch-pagination";
  import { data } from './store';

  // Create the query outside of the render context
  const query = createQuery({
    queryKey: ["posts"],
    queryFn: fetchPaginatedPosts,
    initialData: null,
    refetchInterval: 360000, // Using the intervalMs from the store if needed
  });

  // Subscribe to the data store and update it with the query result
  onMount(() => {
    data.set($query);
  });

  let posts: Post[] = [];

  $: $data = $data || $query.data;

  $: {
    if ($data && !$data.isLoading && !$data.isError && $data.data) {
      posts = $data.data.posts || [];
      console.log("call without API", posts);
    }
  }
</script>

src/routes/post/[id]/[title]/+page.svelte

  <script lang="ts">
  import { page } from "$app/stores";
    import type { Post } from "$lib/models/Post";
    import { QueryCache } from "@tanstack/svelte-query";
    import { onMount } from "svelte";
  
    const queryCache = new QueryCache()
  
  const query = queryCache.find({ queryKey: ['posts']})
  
  let posts: Post[]
  let post: Post
  $: id = $page.params.id;

  onMount(()=>{
    posts = $query.data.posts
    post = posts.find(post.id === id)
  })
  onMount(()=>{
    console.log(post)
  })
  </script>

After getting answer:

src/hooks.server.ts

import type { Handle } from '@sveltejs/kit';
import { fetchPaginatedPosts } from '$lib/posts/fetch-pagination';
import { queryClient } from '$lib/posts/queryClient'; // Assuming you have a separate queryClient instance
import { fetchedPosts } from '$lib/posts/store';
import type { Posts } from '$lib/models/Post';

export const handle: Handle = async ({ event, resolve }) => {
  if (event.url.pathname.startsWith('/posts')) {

    const cachedData = queryClient.getQueryData(['posts']);

    if (!cachedData) {
      const posts = await fetchPaginatedPosts();
      queryClient.setQueryData(['posts'], posts);
        fetchedPosts.set(posts)

    }else{
        fetchedPosts.set(cachedData as unknown as Posts)
  
    }
    
  }


  const response = await resolve(event);
  return response;
};

Upvotes: 0

Views: 77

Answers (2)

Adam Muse
Adam Muse

Reputation: 279

You can use sveltes Middleware (hooks.server.ts). In the middleware get the data you need, and check if the correct data was retrieved for the page if not run it again.

Upvotes: 0

kawa
kawa

Reputation: 611

This will solve:

You need to add: queries: {enabled: browser}

src/routes/+layout.svelte

<script lang="ts">
  import { browser } from '$app/environment'
  import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query'
  // add css import if you need
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        enabled: browser, // this line
      },
    },
  })
</script>

<QueryClientProvider client={queryClient}>
  <slot />
</QueryClientProvider>

Upvotes: 0

Related Questions