Chapter 10: Fetching Data with Svelte

Most applications need to fetch data from APIs. In this chapter, you'll learn how to make API calls, handle loading and error states, and work with asynchronous data in Svelte.

Basic API Calls

Use the Fetch API or third-party libraries to make API calls. Here's a basic example:

<script>
  import { onMount } from 'svelte';

  let data = null;
  let error = null;

  onMount(async () => {
    try {
      const response = await fetch('https://api.example.com/data');
      if (!response.ok) throw new Error('Network response was not ok');
      data = await response.json();
    } catch (err) {
      error = err.message;
    }
  });
</script>

{#if error}
  <p class="error">Error: {error}</p>
{:else if data}
  <p>Data loaded: {JSON.stringify(data)}</p>
{:else}
  <p>Loading...</p>
{/if}

<style>
  .error { color: red; }
</style>

Handling Loading and Error States

Properly manage different states in your component:

<script>
  import { onMount } from 'svelte';

  let loading = false;
  let data = null;
  let error = null;

  onMount(async () => {
    loading = true;
    try {
      const response = await fetch('/api/posts');
      if (!response.ok) throw new Error('Failed to fetch');
      data = await response.json();
    } catch (err) {
      error = err.message;
    } finally {
      loading = false;
    }
  });
</script>

{#if loading}
  <p>Loading...</p>
{:else if error}
  <div class="alert alert-danger">{error}</div>
{:else if data}
  <ul>
    {#each data as item (item.id)}
      <li>{item.title}</li>
    {/each}
  </ul>
{:else}
  <p>No data available.</p>
{/if}

Working with Stores

For complex data management, use Svelte Stores:

// stores.js
import { writable } from 'svelte/store';

export const posts = writable([]);
export const loading = writable(false);
export const error = writable(null);

export async function loadPosts() {
  loading.set(true);
  error.set(null);
  try {
    const response = await fetch('/api/posts');
    const data = await response.json();
    posts.set(data);
  } catch (err) {
    error.set(err.message);
  } finally {
    loading.set(false);
  }
}

Use in components:

<script>
  import { posts, loading, error, loadPosts } from './stores';

  onMount(loadPosts);
</script>

{#if $loading}
  <p>Loading...</p>
{:else if $error}
  <p class="error">{$error}</p>
{:else}
  <ul>
    {#each $posts as post (post.id)}
      <li>{post.title}</li>
    {/each}
  </ul>
{/if}

<style>
  .error { color: red; }
</style>

POST Requests

Send data to an API:

<script>
  let title = '';

  async function addPost() {
    const response = await fetch('/api/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title })
    });
    return await response.json();
  }
</script>

<input bind:value={title} placeholder="Post title" />
<button on:click={addPost}>Add Post</button>

Fetching data in Svelte is straightforward and reactive!