Библиотека svelte-query

svelte-query — это библиотека для управления асинхронными запросами и состоянием данных в приложениях на SvelteKit. Она позволяет удобно выполнять HTTP-запросы, кэшировать результаты, автоматически обновлять данные и управлять состояниями загрузки и ошибок.

Ключевые преимущества использования svelte-query:

  • Кэширование данных — повторные запросы не требуют обращения к серверу, если данные уже загружены.
  • Автообновление — данные можно настроить на автоматическое обновление через заданный интервал.
  • Управление состояниями загрузки и ошибок — нет необходимости вручную отслеживать loading, error и data.
  • Оптимистические обновления — позволяет мгновенно обновлять UI перед подтверждением изменений от сервера.

Установка и настройка

Для интеграции svelte-query в проект SvelteKit нужно установить пакет:

npm install @tanstack/svelte-query

Затем в корневом компоненте приложения (например, +layout.svelte) необходимо подключить QueryClient и QueryClientProvider:

<script lang="ts">
  import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query';

  const queryClient = new QueryClient();
</script>

<QueryClientProvider client={queryClient}>
  <slot />
</QueryClientProvider>

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

Использование useQuery

useQuery — основной хук для получения данных. Он принимает объект с ключом запроса и функцией-фетчером:

<script lang="ts">
  import { useQuery } from '@tanstack/svelte-query';

  const fetchTodos = async () => {
    const res = await fetch('/api/todos');
    if (!res.ok) throw new Error('Ошибка загрузки');
    return res.json();
  };

  const todosQuery = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    staleTime: 1000 * 60 // 1 минута
  });
</script>

{#if todosQuery.isLoading}
  <p>Загрузка...</p>
{:else if todosQuery.isError}
  <p>Ошибка: {todosQuery.error.message}</p>
{:else}
  <ul>
    {#each todosQuery.data as todo}
      <li>{todo.title}</li>
    {/each}
  </ul>
{/if}

Основные свойства объекта запроса:

  • isLoading — активен, пока данные загружаются.
  • isError — true при возникновении ошибки.
  • data — результат запроса.
  • error — объект ошибки.
  • refetch — функция повторного запроса данных.

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

Мутации с useMutation

Для изменения данных на сервере используется useMutation. Она подходит для POST, PUT, PATCH и DELETE-запросов. Пример добавления новой задачи:

<script lang="ts">
  import { useMutation, useQueryClient } from '@tanstack/svelte-query';

  const queryClient = useQueryClient();

  const addTodo = async (newTodo) => {
    const res = await fetch('/api/todos', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newTodo)
    });
    if (!res.ok) throw new Error('Ошибка добавления');
    return res.json();
  };

  const mutation = useMutation(addTodo, {
    onSuccess: () => {
      queryClient.invalidateQueries(['todos']);
    }
  });

  const handleAdd = async () => {
    await mutation.mutateAsync({ title: 'Новая задача' });
  };
</script>

<button on:click={handleAdd}>Добавить задачу</button>

Ключевые моменты:

  • mutateAsync позволяет работать с асинхронными функциями и использовать await.
  • onSuccess и onError — хуки обратного вызова после завершения запроса.
  • queryClient.invalidateQueries автоматически обновляет кэшированные данные после мутации.

Настройка глобального поведения

svelte-query поддерживает глобальные настройки через QueryClient, которые влияют на все запросы:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      retry: 1,
      staleTime: 5 * 60 * 1000
    },
    mutations: {
      retry: 0
    }
  }
});

Основные параметры:

  • refetchOnWindowFocus — повторный запрос при возврате на вкладку браузера.
  • retry — количество попыток повторного запроса при ошибке.
  • staleTime — время, в течение которого данные считаются свежими.

Кэширование и синхронизация

svelte-query хранит результаты запросов в кэше с уникальными ключами (queryKey). При вызове useQuery с уже существующим ключом данные берутся из кэша. Если кэш устарел (staleTime прошёл), автоматически выполняется повторный запрос.

Дополнительные методы работы с кэшем:

  • queryClient.getQueryData(['todos']) — получить текущие данные.
  • queryClient.setQueryData(['todos'], newData) — обновить кэш вручную.
  • queryClient.invalidateQueries(['todos']) — пометить кэш как устаревший для автоматического обновления.

Оптимистические обновления

Оптимистические обновления позволяют мгновенно изменять UI до получения подтверждения от сервера:

const mutation = useMutation(addTodo, {
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries(['todos']);
    const previousTodos = queryClient.getQueryData(['todos']);
    queryClient.setQueryData(['todos'], [...previousTodos, newTodo]);
    return { previousTodos };
  },
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context.previousTodos);
  },
  onSettled: () => {
    queryClient.invalidateQueries(['todos']);
  }
});

Интеграция с SvelteKit endpoints

svelte-query легко интегрируется с +server.ts и +page.ts для загрузки данных через API маршруты. Например:

// src/routes/api/todos/+server.ts
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async () => {
  const todos = await fetchTodosFromDB();
  return new Response(JSON.stringify(todos), { status: 200 });
};

В компоненте:

const todosQuery = useQuery({
  queryKey: ['todos'],
  queryFn: async () => {
    const res = await fetch('/api/todos');
    return res.json();
  }
});

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

Реактивные зависимости запросов

useQuery поддерживает реактивные зависимости через queryKey. Если ключ содержит реактивное значение, запрос будет автоматически обновляться при его изменении:

<script lang="ts">
  import { writable } from 'svelte/store';
  import { useQuery } from '@tanstack/svelte-query';

  const userId = writable(1);

  const userQuery = useQuery({
    queryKey: ['user', $userId],
    queryFn: async ({ queryKey }) => {
      const res = await fetch(`/api/users/${queryKey[1]}`);
      return res.json();
    }
  });
</script>

Изменение значения userId вызовет повторный запрос данных пользователя.

Выводы по работе с svelte-query

svelte-query обеспечивает эффективное управление асинхронными данными, сокращает количество повторных запросов и упрощает синхронизацию состояния между сервером и UI. Основные концепции — useQuery, useMutation, глобальные настройки QueryClient, кэширование и оптимистические обновления — формируют мощный инструмент для построения современных SvelteKit приложений.

Использование реактивных ключей и интеграция с API-эндпоинтами делает библиотеку гибкой и безопасной для масштабирования больших проектов.