4

I have a SvelteKit landing page component that calculates and displays statistics. Because crunching the numbers out from the database is a heavy job, and this information updates only around an hour or so, I would like to cache the result so that the landing page will be served fast for all the visitors. The statistics are loaded from the backend using a fetch() during route load().

I am using SvelteKit server-side rendering for the page, with Node.js adapter. I can have SvelteKit to connect to SQL, Redis or something else to store the result of caching. I believe even a file-system based cache would work in my case.

Do Svelte and SvelteKit support any kind of server-side rendering caching options that make sense in my use case? E.g. instead of rendering the component, the component would cache the load() input or even the generated HTML. Are there libraries or caching solutions that are well-known in Svelte world and integrate well with Svelte?

Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435

3 Answers3

3

You could jump straight into libraries such as svelte-swr or svelte-sswr. Though SvelteKit supports a number of facilities out of the box which you can use to optimize performance closer to the client first, helping eliminate costly server requests while improving user experience.

Start on the client first, then work your way down the stack. Here are some native SvelteKit performance features to look at before adding external dependencies:

Once you've exhausted your options in the browser and via native features, then you could look at adding external SWR dependencies to further optimize performance without masking the performance wins you could achieve using inbuilt and standards-based SvelteKit features.

Alternatively, you could make the server-side calls not cost as much without adding too much complexity by caching the database as you noted in the comments. Here's a post from Geoff discussing how to integrate Redis with a SvelteKit application:

https://blog.upstash.com/svelte-with-serverless-redis

Finally, if you're using something like GraphQL many clients have inbuilt caching facilities which would likely also help improve response time while limiting over- and under-fetching.

vhs
  • 9,316
  • 3
  • 66
  • 70
  • 1
    I do not think this answer is relevant, because the question is about server-side rendering and the answer is about client-side rendering. – Mikko Ohtamaa Apr 18 '22 at 08:08
  • Hi Mikko. I believe your goal was to limit the number of heavy calls on the server. Typically to do this you want a two-pronged approach. Start on the client, then move to the server. I've linked two server-side libraries you could look into once you get there. – vhs Apr 18 '22 at 08:36
  • Hi @vhs - Ok I will take a look. Maybe I misunderstood the answer. – Mikko Ohtamaa Apr 18 '22 at 08:39
  • @MikkoOhtamaa I've updated my answer to clarify what I was trying to communicate. Appreciate your feedback and wish you the best of luck in your search. – vhs Apr 24 '22 at 05:22
  • Thank you. On the lines of question, I am more looking a solution like Redis or Memcached where I can keep hot server-side data so this is not relevant. I still appreciate for the detailed answer. – Mikko Ohtamaa Apr 24 '22 at 07:59
  • 1
    No problem. I linked a post on integrating Redis and added some final thoughts on potentially using GraphQL as opposed to something like REST to further beef of performance. – vhs Apr 24 '22 at 11:38
2

No built in feature exists to my knowledge.

This post might help a little in terms of understanding SSR: SSR explained in SvelteKit

Else here is a template you could use. A simple cache to use would be https://www.npmjs.com/package/timed-cache

  export async function load({ page, fetch, session, stuff }) {

  if (isDataInCache && isDataNotExpired) {
    return {
      props: {
        data: getDataFromCache()
      }
    }; 

  } else {
    const url = `/your-url`;
    const res = await fetch(url);

    if (res.ok) {
      const data = await res.json();

      writeDataToCache(data);

      return {
        props: {
          data
        }
      };
    }

    return {
      status: res.status,
      error: new Error(`Could not load ${url}`)
    };

  }
}
SoluableNonagon
  • 11,541
  • 11
  • 53
  • 98
  • This will only cache the response from the endpoint, but not the generated (HTML) output. – xsonic Jul 02 '22 at 10:38
  • 1
    If the endpoint data is cached then the html generation should be quite quick. If you are looking to cache the HTML then an output cache seems the way to go. – SoluableNonagon Jul 04 '22 at 02:28
1

You could set up Redis. Creating HTML files on server is an expensive operation and sveltekit does not cache it.(Next.js does cache html files)

When a user visits your application, rather than generating the page your app should be checking if you already created some HTML for that page and stored it inside of Redis. If the page is already created, you would be sending it immediately to the server. If not, your app will be generating and then store that HTML inside of Redis.

Some pages needs new content for every view, some does not, for example contact page would be same for all users but user dashboard will be different for each user. So caching might not be idea for all pages. Imagine your application has 100.000 users for each you cache the dashboard page, you would need a tremendous amount of space.

It is good idea to cache only the static pages and with redis you can expire the cache data after a specified time.

You set the redis client:

import { createClient } from 'redis';

const client = createClient({
    socket: {
        host: redisHost,
        port: redisPort
    },
    password: redisPassword
});

client.on('error', (err) => console.error(err));
client.connect();

then write get and set methods.

// determine the static pages of your app
const routes = ['/about', '/contact'];
export const getCachedPage = (route: string) => {
    if (routes.includes(route)) {
        // naming key value in redis is up to you
        return client.get('pagecache' + route);
    }
    return null;
};

export const setCachedPage = (route: string, page: string) => {
    if (routes.includes(route)){
        // set(key,value)
        // set expire time in EX:
        return client.set('pagecache' + route,page,{EX:3000})
    }
};

   
Yilmaz
  • 35,338
  • 10
  • 157
  • 202