Skip to content

useQuery

Data fetching with caching, stale-while-revalidate, retries, and refetch triggers. Inspired by TanStack Query. Use for API data that benefits from caching and invalidation.

Basic Usage

vue
<script setup>
import { useQuery } from "@baklavue/composables";

const { data, error, isFetching, isLoading, refetch } = useQuery({
  queryKey: ["users"],
  queryFn: () => fetch("/api/users").then((r) => r.json()),
});
</script>

<template>
  <div v-if="isLoading">Loading...</div>
  <div v-else-if="error">Error: {{ error.message }}</div>
  <div v-else-if="data">
    <ul>
      <li v-for="user in data" :key="user.id">{{ user.name }}</li>
    </ul>
  </div>
  <button @click="refetch">Refresh</button>
</template>

Reactive Query Key

Use a computed or getter for reactive query keys (e.g. when key depends on route params):

vue
<script setup>
import { useQuery } from "@baklavue/composables";

const userId = ref("1");
const { data } = useQuery({
  queryKey: () => ["users", userId.value],
  queryFn: ({ queryKey }) =>
    fetch(`/api/users/${queryKey[1]}`).then((r) => r.json()),
});

// Refetches automatically when userId changes
</script>

Stale Time

Set staleTime to avoid refetching while data is fresh:

vue
<script setup>
import { useQuery } from "@baklavue/composables";

const { data } = useQuery({
  queryKey: ["users"],
  queryFn: () => fetch("/api/users").then((r) => r.json()),
  staleTime: 60_000, // Data stays fresh for 1 minute
});
</script>

Dependent Queries

Use enabled to run a query only when dependencies are ready:

vue
<script setup>
import { useQuery } from "@baklavue/composables";

const userId = ref(null);
const { data } = useQuery({
  queryKey: () => ["users", userId.value],
  queryFn: ({ queryKey }) =>
    fetch(`/api/users/${queryKey[1]}`).then((r) => r.json()),
  enabled: () => userId.value != null,
});
</script>

Cache Invalidation

Use useQueryClient to invalidate or update cached data:

vue
<script setup>
import { useQuery, useQueryClient } from "@baklavue/composables";

const queryClient = useQueryClient();

// Invalidate all queries matching ["users"]
const invalidateUsers = () => {
  queryClient.invalidateQueries({ queryKey: ["users"] });
};

// Manually set cache data
const setUser = (id, user) => {
  queryClient.setQueryData(["users", id], user);
};

// Read cached data
const cachedUser = queryClient.getQueryData(["users", 1]);
</script>

Polling (refetchInterval)

Poll data at a fixed interval:

vue
<script setup>
import { useQuery } from "@baklavue/composables";

const { data } = useQuery({
  queryKey: ["live-status"],
  queryFn: () => fetch("/api/status").then((r) => r.json()),
  refetchInterval: 5000,
  refetchIntervalInBackground: false, // Pause when tab is hidden
});
</script>

Prefetch

Preload data before navigation (e.g. on link hover):

vue
<script setup>
import { useQueryClient } from "@baklavue/composables";

const queryClient = useQueryClient();

const prefetchUser = (id) => {
  queryClient.prefetchQuery({
    queryKey: ["users", id],
    queryFn: () => fetch(`/api/users/${id}`).then((r) => r.json()),
  });
};
</script>

<template>
  <a href="/user/1" @mouseenter="prefetchUser(1)">User 1</a>
</template>

Refetch Triggers

By default, queries refetch when the window regains focus or when the network reconnects. Disable with options:

vue
<script setup>
import { useQuery } from "@baklavue/composables";

const { data } = useQuery({
  queryKey: ["users"],
  queryFn: () => fetch("/api/users").then((r) => r.json()),
  refetchOnWindowFocus: false,
  refetchOnReconnect: false,
});
</script>

Retries

Configure retry behavior:

vue
<script setup>
import { useQuery } from "@baklavue/composables";

// Retry 3 times with exponential backoff (default)
const { data } = useQuery({
  queryKey: ["users"],
  queryFn: () => fetch("/api/users").then((r) => r.json()),
  retry: 3,
});

// No retries
const { data: data2 } = useQuery({
  queryKey: ["config"],
  queryFn: () => fetch("/api/config").then((r) => r.json()),
  retry: false,
});
</script>

API

useQuery

typescript
useQuery<T>(options: UseQueryOptions<T>): UseQueryReturn<T>

Options

PropertyTypeDefaultDescription
queryKeyMaybeRefOrGetter<QueryKey>requiredUnique key for caching (array of values)
queryFn(context: { queryKey }) => Promise<T>requiredAsync function that fetches data
staleTimenumber0ms until data is stale (0 = always stale)
retrynumber | boolean3Retries on error (false = none)
retryDelaynumber | (attempt, error) => numberexponential backoffDelay between retries in ms
enabledMaybeRefOrGetter<boolean>trueIf false, query won't run
refetchOnWindowFocusbooleantrueRefetch when window regains focus
refetchOnReconnectbooleantrueRefetch when network reconnects
refetchIntervalnumber0Polling interval in ms (0 = no polling)
refetchIntervalInBackgroundbooleanfalseContinue polling when tab is hidden
initialDataT | (() => T)-Initial data before first fetch

Return Value

PropertyTypeDescription
dataRef<T | null>Fetched data
errorRef<Error | null>Error if request failed
isFetchingRef<boolean>True while request is in flight
isLoadingRef<boolean>True when no data yet and fetching
isSuccessRef<boolean>True when data is available
isErrorRef<boolean>True when error occurred
refetch() => Promise<void>Manual refetch

useQueryClient

typescript
useQueryClient(): QueryClient

QueryClient Methods

MethodDescription
invalidateQueries(options?)Remove matching entries from cache. { queryKey: ["users"] } invalidates all keys starting with ["users"]
getQueryData<T>(queryKey)Get cached data for a key
setQueryData<T>(queryKey, data)Manually set cache for a key
prefetchQuery(options)Prefetch and cache data. { queryKey, queryFn }

useQuery vs useFetch

useQueryuseFetch
CachingYes, by query keyNo
Stale / refetchYesNo
RetriesConfigurableConfigurable (opt-in)
Refetch triggersWindow focus, reconnect (default on)Window focus, reconnect, URL change (opt-in)
InvalidationVia useQueryClientN/A
Use caseCached API dataOne-off or non-cached requests