SvelteKit 2—Further Adventures with SvelteKit

SvelteKit Machine

This post continues from SvelteKit 1. If you want to code along, copy the code from that post.

At this point, we have a simple, two-page website. Let’s take it just a bit further.

First of all, we would like each of our pages to have an appropriate title and a description for SEO. If you look at the file app.html, you will see there is an entry, %svelte.head% in the head section. We can insert code there by adding these lines to the top of index.svelte, our home page:

<svelte:head>
<title>My Great App</title>
<meta
    name="description"
    content="I'm still learning SvelteKit, but I've already built this website."
/>
</svelte:head>

If we had a <script> Section on our page, we might place these lines after the </script> tag.

Do the same thing on the About page, but make the title, "About My Great App", and the description content something you like, such as "About Building SvelteKit Skills."

Getting External Data

So far, our simple two-page website is totally static—that is, every viewer who sees it, and every time it is seen, exactly the same content displays, and all this content is contained within the site itself. But generally, to create a useful web app, we need the ability either to bring in data from someplace, or to collect and save data someplace, or both.

There are many ways in which data might be acquired or saved. The data might be saved as a .json file as part of your site, in which case the endpoint would simply fetch that file. You might access a database, either through direct database calls (my next blog post will demonstrate that) or by making RESTful calls. Or you might call a web api provided by someone. We will use this last approach in this post.

The simplest case is if we need some external data when a page is first loaded, but don't need to retrieve it over again during that session. This can be handled by using Svelte's onMount function, which is the commonest of Svelte's lifecycle functions.

Let's add a few dad jokes to our About page. To do this, we will call on a simple web api. As is common, this web api will return data to us in JSON format, which we will then display on the page. To learn about this simple api, visit its documentation.

From that documentation, we see that we can search for dad jokes containing a string, and see the format of the results. So modify your about page to have the following code:

<script>
    const url = "https://icanhazdadjoke.com/search?term=about";
    import { onMount } from "svelte";
    let jokes = {
        results: [],
    };

    onMount(async () => {
        const response = await fetch(url, {
            headers: {
            Accept: "application/json",
            "User-Agent": "Learning exercise, myname@xxxxxx.com",
            },
        });
        jokes = await response.json();
    });
</script>
<h1>A Random Dad Joke</h1>
<p><i>This is About as bad as it gets.</i></p>
{#each jokes.results as joke}
    <p style="font-size:1.4em;">{joke.joke}</p>
{/each}

This will put a list of 20 dad jokes on our page, all of which contain the string "about". The onMount function is run when the page has been mounted in the DOM. This means that the data is not loaded during server-side rendering, but lazy loaded. We see that it calls our api, and places the return data in the jokes variable. Within the HTML, the {#each} statement, terminated by {/each}, loops through the array in jokes.results and outputs the HTML between the opening and closing each statements for each item in that array.

But there are many situations where we do not want to get the same results every time we visit the page. We need our data to be gotten dynamically. In SvelteKit, these situations are handled by endpoints. An endpoint is just a JavaScript (or TypeScript) file with a conventional .js (or .ts) suffix. These files will be run from the server, while a .svelte file might be run either from the server or from the client.

There are two ways of using endpoints in SvelteKit. In one case, the endpoint needs to be called each time the page is visited, and so it is directly associated with the page. This is done by naming it the same as the page, but with the .js or .ts suffix.

The other approach is if the endpoint needs to be called from multiple places, or be called on request from a page (say, when a button is clicked).

Let's change the About page to show a random dad joke each time it is visited. In your src directory, create a new file called about.js, and put the following code in it:

const url = "https://icanhazdadjoke.com/";
export async function get({ params }) {
  const response = await fetch(url, {
    headers: {
      Accept: "application/json",
      'User-Agent': "Learning exercise, myname@xxxxxx.com",
    },
  });
  const joke = await response.json();
  if (joke) {
    return {
      body: { joke },
    };
  }
  return {
    status: 404,
  };
}

This is our endpoint. What we are exporting here is a request handler function whose name would be get, post, or whatever HTTP method it corresponds to. This will be automatically invoked when the About page is navigated to. As a courtesy to the api developer, please update the User-Agent with a website or email address per the documentation.

Now we need to modify the About page to accept the return from this function. This will be passed to it as a prop, which is indicated by calling it an export on our page file. So replace the contents of our about.svelte with the following code:

<script>
export let joke;
</script>
<h1>A Random Dad Joke</h1>
<p><i>This is About as bad as it gets.</i></p>
<p style="font-size:2em;">{joke.joke}</p>

At this point, navigating to your About page should display a random dad joke each time you navigate there.

There will be a slight lag when going to the About page. One thing we can do to speed this up is to prefetch the page whenever the user hovers over the menu item (or when it is clicked, without waiting for the click event). This is easily done by changing the link in __layout.svelte to <a sveltekit:prefetch href="/about">About</a> Voila! This likely saves a couple tenths of a second in load time, enough to give the impression of a snappy response rather than a slow one.

We haven't yet covered the scenario of an endpoint that can be called at any time. To learn that, check out my upcoming blog post on using MySQL with SvelteKit.