Fetch и обработка запросов

В SvelteKit fetch используется как стандартный способ получения данных с сервера, как на стороне клиента, так и на серверной стороне (в load функциях). Основное отличие заключается в контексте вызова: при серверном fetch запросы могут обходить CORS и работать напрямую с внутренними API, а при клиентском — соблюдаются обычные ограничения браузера.

Пример базового использования в load функции:

export async function load({ fetch }) {
    const response = await fetch('/api/data');
    if (!response.ok) {
        throw new Error('Ошибка загрузки данных');
    }
    const data = await response.json();
    return { props: { data } };
}

Здесь важно: fetch внутри load автоматически проксирует относительные пути к серверу приложения, что упрощает работу с внутренними эндпоинтами.


Обработка ошибок и статусов

Обработка ошибок должна учитывать не только сетевые ошибки, но и HTTP-статусы:

export async function load({ fetch }) {
    try {
        const res = await fetch('/api/items');
        if (!res.ok) {
            return { props: { items: [], error: res.statusText } };
        }
        const items = await res.json();
        return { props: { items } };
    } catch (err) {
        return { props: { items: [], error: err.message } };
    }
}

Ключевой момент: при серверной загрузке throw error(status, message) можно использовать для генерации страницы с соответствующим статусом HTTP:

import { error } from '@sveltejs/kit';

export async function load({ fetch }) {
    const res = await fetch('/api/secure-data');
    if (res.status === 401) {
        throw error(401, 'Неавторизованный доступ');
    }
    const data = await res.json();
    return { props: { data } };
}

Асинхронные запросы и параллельная загрузка

Для ускорения загрузки нескольких ресурсов можно использовать Promise.all:

export async function load({ fetch }) {
    const [usersRes, postsRes] = await Promise.all([
        fetch('/api/users'),
        fetch('/api/posts')
    ]);

    const [users, posts] = await Promise.all([
        usersRes.json(),
        postsRes.json()
    ]);

    return { props: { users, posts } };
}

Это особенно полезно для больших дашбордов, где одновременно нужны несколько источников данных.


Использование fetch в API маршрутах SvelteKit

Внутри серверных маршрутов (+server.js или +server.ts) fetch можно использовать для запроса внешних API:

export async function GET({ fetch }) {
    const res = await fetch('https://jsonplaceholder.typicode.com/todos');
    if (!res.ok) {
        return new Response('Ошибка запроса', { status: res.status });
    }
    const todos = await res.json();
    return new Response(JSON.stringify(todos), { headers: { 'Content-Type': 'application/json' } });
}

Серверный fetch позволяет передавать дополнительные заголовки, токены аутентификации и работать с внутренними API без ограничений браузера.


Работа с заголовками и авторизацией

Для авторизованных запросов часто используется объект Headers:

const token = 'Bearer your-jwt-token';
const res = await fetch('/api/private', {
    headers: {
        'Authorization': token,
        'Accept': 'application/json'
    }
});
const data = await res.json();

При этом важно помнить, что при серверном fetch можно безопасно использовать секреты, которые не должны попадать на клиентскую сторону.


Интеграция с SvelteKit UI библиотеки

При использовании UI библиотек (например, SvelteKit UI, Skeleton или Flowbite Svelte) данные часто подгружаются через fetch и связываются с компонентами через stores или реактивные переменные:

<script>
    import { onMount } from 'svelte';
    import { writable } from 'svelte/store';

    const users = writable([]);
    const error = writable(null);

    onMount(async () => {
        try {
            const res = await fetch('/api/users');
            if (!res.ok) throw new Error(res.statusText);
            users.set(await res.json());
        } catch (e) {
            error.set(e.message);
        }
    });
</script>

{#if $error}
    <p class="error">Ошибка: {$error}</p>
{:else}
    <ul>
        {#each $users as user}
            <li>{user.name}</li>
        {/each}
    </ul>
{/if}

Использование реактивных переменных или writable store позволяет автоматически обновлять UI при изменении данных, что идеально сочетается с компонентами библиотек интерфейса, поддерживающими динамическое отображение списков, таблиц и карточек.


Кэширование и оптимизация

SvelteKit позволяет использовать стратегию кэширования серверных запросов через fetch с дополнительными опциями:

const res = await fetch('/api/data', {
    headers: { 'Cache-Control': 'max-age=60' }
});

Также на клиентской стороне можно использовать svelte-query или @tanstack/svelte-query для управления кэшом данных, автоматической повторной загрузки и синхронизации состояния с UI-компонентами.


Особенности работы с относительными и абсолютными URL

  • Относительные пути (/api/data) внутри load автоматически направляются к серверу приложения.
  • Абсолютные URL (https://example.com/api/data) могут использоваться как на сервере, так и на клиенте, но на клиенте потребуется CORS разрешение.
  • Для внутреннего кэширования и SSR рекомендуется использовать относительные пути, чтобы fetch проходил через сервер SvelteKit.

Заключение ключевых моментов работы с Fetch

  • fetch в load функциях может работать как на сервере, так и на клиенте, с разными ограничениями.
  • Обработка ошибок и статусов должна быть обязательно явной, особенно при работе с API.
  • Параллельные запросы через Promise.all значительно ускоряют загрузку больших наборов данных.
  • Серверный fetch позволяет безопасно использовать секреты и авторизацию без попадания на клиент.
  • Интеграция с UI библиотеками строится на реактивных переменных или store, обеспечивая динамическое обновление интерфейса при изменении данных.
  • Кэширование и управление состоянием через сторонние библиотеки повышают производительность и UX.

Этот подход делает работу с fetch в SvelteKit эффективной, безопасной и удобной для интеграции с современными UI библиотеками.