Skip to content

useFetch

Reactive fetch with loading state, error handling, and abort support. Axios-like API with method, headers, body, params, baseURL, interceptors, retries, and refetch triggers.

Basic Usage

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

const { data, error, isFetching, execute } = useFetch("/api/users", {
  immediate: true,
});

// Refetch manually
const refresh = () => execute();
</script>

<template>
  <div v-if="isFetching">Loading...</div>
  <div v-else-if="error">Error: {{ error.message }}</div>
  <div v-else-if="data">{{ data }}</div>
</template>

POST Request

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

const { data, error, isFetching, execute } = useFetch("/api/users", {
  method: "POST",
  body: { name: "John", email: "john@example.com" },
  immediate: false,
});

const createUser = () => execute();
</script>

Base URL and Params

Use baseURL and params for a cleaner API:

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

const { data } = useFetch("/users", {
  baseURL: "https://api.example.com",
  params: { page: 1, limit: 10 },
});
</script>

Preconfigured Instance (createFetch)

Create a reusable fetch instance with default options, similar to axios.create():

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

const useApiFetch = createFetch({
  baseURL: "/api",
  headers: { "X-Custom-Header": "value" },
});

// All requests use baseURL and headers
const { data } = useApiFetch("/users", { params: { page: 1 } });
</script>

Auth Interceptor

Use beforeFetch to add auth or modify the request:

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

const token = ref("your-jwt-token");

const useAuthFetch = createFetch({
  beforeFetch: ({ options }) => {
    (options.headers as Record<string, string>)["Authorization"] =
      `Bearer ${token.value}`;
  },
});

const { data } = useAuthFetch("/me");
</script>

Reactive URL

Pass a getter function for reactive URLs (e.g. when URL depends on route params):

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

const userId = ref("1");
const { data, execute } = useFetch(
  () => `https://api.example.com/users/${userId.value}`,
  { immediate: true }
);

watch(userId, () => execute());
</script>

Retry

Configure retries for transient failures (network errors, 5xx). By default only GET retries; set retryOnNonIdempotent: true for POST/PUT/PATCH:

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

const { data, error } = useFetch("/api/users", {
  retry: 3,
  retryDelay: (attempt) => 1000 * 2 ** attempt,
});
</script>

Refetch Triggers

Opt-in refetch on window focus or network reconnect:

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

const { data } = useFetch("/api/config", {
  refetchOnWindowFocus: true,
  refetchOnReconnect: true,
});
</script>

Refetch on URL Change

When using a reactive URL getter, auto-refetch when the URL changes:

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

const userId = ref("1");
const { data } = useFetch(
  () => `https://api.example.com/users/${userId.value}`,
  { refetchOnUrlChange: true }
);
</script>

Execute with Overrides

Pass dynamic url, body, or params at execute time:

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

const { data, execute } = useFetch("/api/users", {
  method: "POST",
  immediate: false,
});

const createUser = (name: string) =>
  execute({ body: { name }, throwOnFailed: true });
</script>

Error Interceptor

Use onFetchError to log, transform, or handle errors:

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

const { data, error } = useFetch("/api/users", {
  onFetchError: ({ response, error }) => {
    console.error("Fetch failed:", response?.status, error.message);
  },
});
</script>

Manual Execute

Set immediate: false to prevent the request from firing until execute() is called:

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

const { data, execute } = useFetch("/api/users", {
  immediate: false,
});

const loadUsers = () => execute();
</script>

API

typescript
useFetch<T>(
  url: string | (() => string),
  options?: UseFetchOptions
): UseFetchReturn<T>

createFetch(defaultOptions: UseFetchOptions): (url, options?) => UseFetchReturn<T>

Options

PropertyTypeDefaultDescription
immediatebooleantrueRun fetch immediately on creation
initialDataunknownnullInitial data before request completes
timeoutnumber0Timeout in ms to abort (0 = no timeout)
method'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE''GET'HTTP method
headersRecord<string, string>{}Request headers
bodyunknown-Request body (JSON, FormData, string, etc.)
paramsRecord<string, string | number | boolean | array>-URL query params
baseURLstring-Base URL prepended to relative URLs
validateStatus(status: number) => boolean200-299Custom status validation
beforeFetch(ctx: { url, options }) => void | Promise<void>-Hook to modify request before fetch
afterFetch(ctx: { response, data }) => unknown | Promise<unknown>-Hook to transform response data
onFetchError(ctx: { response, error }) => void | Promise<void>-Hook called on fetch error
retrynumber | boolean0Number of retries (or false for none)
retryDelaynumber | (attempt, error) => numberexponential backoffDelay between retries
retryCondition(error, response) => booleannetwork/5xxWhen to retry
retryOnNonIdempotentbooleanfalseRetry POST/PUT/PATCH/DELETE
refetchOnWindowFocusbooleanfalseRefetch when window regains focus
refetchOnReconnectbooleanfalseRefetch when network reconnects
refetchOnUrlChangebooleanfalseRefetch when URL (getter) changes
responseType'json' | 'text' | 'blob' | 'arraybuffer'autoResponse parsing
credentialsRequestCredentials-CORS credentials

Return Value

PropertyTypeDescription
dataRef<T | null>Response data on success
errorRef<Error | null>Error if request failed
statusCodeRef<number | null>HTTP status of last response
isFetchingRef<boolean>True while request is in flight
isFinishedRef<boolean>True when request has finished
execute(options?) => Promise<void>Execute manually; pass { url?, body?, params?, throwOnFailed? } for overrides
abort() => voidAbort the current request