Fetching Data in Svelte
Tags: svelte, webdev, javascript
Published: 2021-02-17 18:11:41

I always find it frustrating when [I'm learning a new framework](http://bit.ly/2NxTxyW) and I get to the point where I have to depart from my existing knowledge and principles in order to learn the framework's way of thinking. Frameworks should exist for developers and so it was refreshing to discover that Svelte is mostly unopinionated when it comes to fetching server data. This means that developers can focus on creating high-quality applications instead of learning “the Svelte way” of fetching data.

## The Tech

Because Svelte doesn't have an out of the box solution for fetching data, it is possible to use almost any existing library to fetch data in your Svelte component. Even though almost everything is possible, `fetch` and `XMLHTTPRequest` are the easiest ways to fetch data in your Svelte component. In this article, I will examine specifically how we can use `fetch` to load data from the [The Star Wars API](https://swapi.dev/), a publicly accessible API for Star Wars data.

## Code Along

You can find working examples of all the code in this article [on github](http://bit.ly/3jVywu9): https://github.com/davidturissini/svelte-data-demo

Clone the repo and then run `npm install && npm run dev`. The app will be running at `http://localhost:5000`.

After reading this article you will be able to:
- Load server data in your Svelte component
- Load server data inside Svelte's `onMount` lifecycle hook
- Display a loading screen while data is being loaded
- Load data on-demand from a user interaction

> This article only covers fetching data for your Svelte components. If you are interested to learn more about Svelte and state management, be sure to follow me to get notified when that article gets published!


## Using Fetch with Svelte
[Code on Github](http://bit.ly/3bdIeE5)

The easiest way to use `fetch` in your Svelte component is to simply invoke `fetch` directly in your component's `<script>` tag. [You'll recall that Svelte's reactivity model](http://bit.ly/2NxTxyW#reactivity) works by referencing a `let` variable directly in your component's HTML. Whenever the variable gets a new value, Svelte will automatically re-render that new value.

With a modified approach, we can use this same functionality to render the contents of a `fetch` response. Instead of immediately assigning a value to our `let` variable, we instead dispatch a `fetch` request. We then populate our variable with values from our response once it settles. Our new values will then automatically be rendered into our HTML and become visible to the user.

For example, to display Luke Skywalker's name in a Svelte component, we can create the variable `characterName` and then make a `fetch` call to `https://swapi.dev/api/people/1`. After our response is settled, we can then assign `character.name` to `characterName`. Since `characterName` is referenced in our HTML, Svelte will render the value for us. Fairly simple!

```html
<script>
	let characterName;

	fetch('https://swapi.dev/api/people/1')
		.then((response) => response.json())
		.then((character) => {
			characterName = character.name;
		})
</script>
<main>
	{characterName}
</main>
```

This approach is not limited to just `fetch`. If we wanted to, we could create a Redux subscription and update `characterName` whenever we are passed a new value. We could also create a GraphQL subscription and follow the same pattern. As long as we can update variables that are in our HTML, Svelte will continue rendering the latest data no matter how we received those values.

## Component onMount
[Code on Github](http://bit.ly/3jUye6I)

Executing `fetch` in your `<script>` tag works well if you know that your component will always run in the browser. If it is even remotely possible that your component will be server rendered, we need to find a different approach. The biggest drawback with invoking `fetch` directly in your `<script>` that `fetch` will also be invoked when your component is rendered on the server. This could lead to some noticeable performance problems, both for your users and your servers.

We can improve our approach above by invoking our `fetch` call inside of [Svelte's onMount lifecycle hook](https://svelte.dev/tutorial/onmount). With the exception of `onDelete`, Svelte lifecycle hooks are never invoked on the server, so putting our `fetch` call inside of an `onDelte` hook guarantees that we will only call our APIs when the component is rendered in the browser. This will reduce your server load because we are only making our `fetch` call once the component is rendered in the browser. It also reduces time to serve because our server code does not have to wait for our data to settle before sending something back to the user.

```html
<script>
    import { onMount } from 'svelte';
	let characterName;

    onMount(async () => {
        const response = await fetch('https://swapi.dev/api/people/1');
        const character = await response.json();
        characterName = character.name;
    })
</script>
<main>
	{characterName}
</main>
```

## Handle Loading States
[Code on Github](http://bit.ly/3bh3j0m)

Even if we use `onMount` to fetch server data, we aren't quite giving our users the best possible experience. Because `characterName` is not initialized with a default value, Svelte will render text `"undefined"` while our app fetches our data. Not ideal!  We could avoid this by giving `characterName` some default value that is displayed while we fetch our data. That approach would work, and it would definitely be a better experience, but I think using an [if-else conditional](https://svelte.dev/tutorial/else-if-blocks) in our HTML to add a spinner would be an even better user experience. This approach is pretty powerful because there is no limit to what you can display while data is being fetched. It can be some simple text or it can be a complex Svelte component.

```html
<script>
	import { onMount } from 'svelte';
	let characterName;

    onMount(async () => {
        const response = await fetch('https://swapi.dev/api/people/1');
        const character = await response.json();
        characterName = character.name;
    });
</script>

<main>
    {#if characterName === undefined}
        Loading Character Name...
    {:else}
        {characterName}
    {/if}
</main>
```

## Lazy HTTP Requests
[Code on Github](http://bit.ly/3puPPmE)

Invoking our `fetch` call inside of `onMount` means that every time our component mounts, we are going to make a server request. This is not always the correct behavior. Sometimes, we might want to wait for our users to give us a signal that they are ready for some data to be loaded. In this case, we can give our users some sort of UI, like a button, to control when our `fetch` call is invoked.

Instead of invoking our fetch call directly in `onMount`, we can make our `fetch` request lazy by moving it inside of a function that can be used as [an event handler](http://bit.ly/2NxTxyW#event-handling).

Making our `fetch` request lazy is a nice performance win. It reduces our server load and perceived user performance because we are not consuming memory or server resources with data that our user may never use. It _also_ exposes an assumption that we made in our code. Up until now, all of our code samples have assumed that we are either making an HTTP request or the HTTP request has settled. Making our `fetch` lazy means that it is possible for our code to be idle. In our case, our idle state is just a period of time before the initial `fetch` request is triggered. In this state, we don't need to show a loading indicator and we don't yet have data to show the user so we need to update our code to handle this new behavior. There are many approaches that we could use, but the easiest way is to simply move `characterName` and `loading` into a tuple. We can then update our HTML conditional to not show our loading screen if `loadig` is false AND `characterName` is not present.


```html
<script>
	let data = {
        characterName: undefined,
        loading: false,
    };

    async function loadData() {
        data.loading = true;
        const response = await fetch('https://swapi.dev/api/people/1')
        const character = await response.json();
        data = {
            characterName: character.name,
            loading: false,
        };
    }
</script>

<main>
    <button on:click={loadData}>Load Data</button>
    {#if data.loading === true}
        Loading Character Name...
    {:else if data.characterName !== undefined}
        {data.characterName}
    {/if}
</main>
```

Now our component waits for our user to click on our `<button>` before making an HTTP request. This is also a good pattern for making create, update, or delete server calls. We certainly wouldn't want our component to be mutating data every time it loads!


## Conclusion

Svelte is very flexible when it comes to fetching data for your application. By and large, you bring whichever tools you are comfortable with and you don't need to reinvent the wheel to render your data. The easiest approach to loading data is by using `fetch` in our `<script>` tag but the most robust way to fetch data is to use `onMount`. Svelte also makes it easy to render a loading screen while our data is being fetched and to make our `fetch` requests lazy which improves our application’s overall performance. If there are any other tips or suggestions, feel free to leave them in the comments below!

Don't forget to FOLLOW if you want to get more Svelte tutorials and deep dives as soon as they are published!