DEV Community

Cover image for How SvelteKit makes type-safe data fetching easier and better!
ashish
ashish

Posted on • Updated on

How SvelteKit makes type-safe data fetching easier and better!

Introduction

It’s been around a month since SvelteKit v1 was released and I fortunately got enough time to play around with it. In this blog post, I would be writing about the data fetching/loading ways in SvelteKit and how SvelteKit makes it easier along with providing type-safety all the way!

Creating a new SvelteKit app

Let’s start from the basics in case you know nothing about Svelte. I’ll be creating a new SvelteKit app first. You can use this command to do it as well (make sure you opt-in to use typescript when prompted) -

pnpm create svelte@latest // or npm create svelte@latest
Enter fullscreen mode Exit fullscreen mode

Now that we have our project setup, let’s build a new page. SvelteKit uses a file-based routing just like other frameworks like Next.js. Head over to src/routes/+page.svelte in your app. This is the starting point of any SvelteKit app - the Index page. I won’t be creating any more routes as we don’t need them for this blog post. You can learn more about routing from SvelteKit docs. Start the dev server for the app and if you did everything correctly you should be seeing a page like this if you head over to localhost:5173.

SvelteKit default index page

Understanding data loading in SvelteKit

Now let’s start with the actual topic of this blog post - data fetching. If you read the SvelteKit docs for data fetching, it states that you need separate files for loading your data which can be created for every route - “A +page.sveltefile can have a sibling +page.js(or +page.ts) that exports a loadfunction, the return value of which is available to the page via the data prop”. Didn’t get it? don’t worry, let’s break it into parts.

So, first of all for every route that you want to load/fetch external data, you need a separate file. For example, for every +page.svelte in your routes where you need external data, you will need a corresponding +page.ts file for loading its data. The second part says something about a loadfunction. A load function is the function that you export from your +page.ts file returning the loaded data. SvelteKit autogenerates the return types for you which can be imported in your component file using $types module. I think it might be making some sense at this point, but let’s see this in action.

Do it yourself

Let’s create a new route todo in our app and create +page.svelte and +page.ts files inside it. I’ll be using dummyjson to mimic an external database.

// src/routes/todo/+page.ts

import type { PageLoad } from './$types';

type todo = {
    id: number;
    todo: string;
    completed: boolean;
    userId: number;
}; // todo type for better type safety

type t = {
    todos: todo[];
    total: number;
    skip: number;
    limit: number;
}; // actual json reponse type 

export const load: PageLoad = async () => {
    const r = await fetch('https://dummyjson.com/todos');
    const todos = ((await r.json()) as t).todos; // we only want to send the todo array as props
    return {
        props: {
            todos
        }
    };
};
Enter fullscreen mode Exit fullscreen mode
<!-- src/routes/todo/+page.svelte -->

<script lang="ts">
    import type { PageData } from './$types';

    export let data: PageData;
</script>

<h1>Todo List</h1>
{#each data.props.todos as todo (todo.id)}
    <div>
        <p>{todo.todo}</p>
    </div>
{/each}
Enter fullscreen mode Exit fullscreen mode

If you copy this code to your editor, you would be able to see some beautiful typescript autocompletion working in here. Let’s try to understand the code.

Understanding the code

First of all I created a +page.ts file inside my todo route and created a load function of the type PageLoad . I have fetched the json response from dummyjson inside the function and returned an array of todos from the response. The type t at the beginning is optional and you may not use it but the type todo is important if you want end-to-end type-safety (also it’s a good practice to keep all the types in a separate file and import them, instead of cluttering the main file).

SvelteKit then generates the types for the load function which could be used in our +page.svelte. In the src/routes/todo/+page.svelte , we first need to import the generated types and then export it as data . The next part is pretty easy if you already know Svelte syntax, we just need to iterated over the array of list and list them down.

If you do everything correctly and then head over to localhost:5173/todo, you would be able to see something like this -

Todo List

Fetching data server side

So, our data fetching works perfectly fine. You could experiment a bit more with it if you want. But I’ll head over to the last subtopic of the blog which is loading data server side only. In many cases, you would not want your load function to not run client side (because it uses private environment variables, for example, or accesses a database). For such cases, SvelteKit provides another similar way for loading data.

To do so, instead of creating a +page.ts , you’ll just have to create a +page.server.ts and change the function type a bit like this -

// src/routes/todo/+page.server.ts

import type { PageServerLoad } from './$types';

type todo = {
    id: number;
    todo: string;
    completed: boolean;
    userId: number;
}; // todo type for better type safety

type t = {
    todos: todo[];
    total: number;
    skip: number;
    limit: number;
}; // actual json reponse type 

export const load: PageServerLoad = async () => {
    const r = await fetch('https://dummyjson.com/todos');
    const todos = ((await r.json()) as t).todos; // we only want to send the todo array as props
    return {
        props: {
            todos
        }
    };
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

You can learn more about the PageLoad and PageServerLoad types from the docs. If you are new to Svelte and SvelteKit, this might have been a bit harder to understand (maybe) or maybe even some of you wouldn’t like this way of loading data (why do we need to create separate files????). But, believe me, once you start using it, it just becomes natural as your app just gets more organized and I don’t think it’s that bad considering the type-safety you get along with it without using any third party library.

This is it for this blog post, I hope I was able to explain the topic properly. In case you don’t understand something or think something’s not right, feel free to send a comment my way!

Thanks a lot for reading!

Top comments (2)

Collapse
 
deezy profile image
DEE

Good one Chief💪🏾.
You and @nexxeln really inspire me.

Collapse
 
asheeshh profile image
ashish