Что такое утилита $fetch и чем она отличается от нативного fetch?
$fetch — это глобальный алиас ofetch в Nuxt 3: автоматически парсит JSON, выбрасывает типизированные ошибки и поддерживает интерцепторы, в отличие от нативного fetch. Для SSR-компонентов его нужно оборачивать в useAsyncData, иначе запрос выполнится дважды.
Что такое $fetch
$fetch в Nuxt 3 — это глобальный алиас для ofetch, HTTP-клиента от команды UnJS. Он автоматически доступен в любом месте приложения без импорта. В отличие от нативного fetch, ofetch предоставляет:
- Автоматический парсинг JSON (и FormData, и text — в зависимости от Content-Type).
- Умную обработку ошибок: выбрасывает объект FetchError с полями
status,statusText,dataвместо необходимости проверятьresponse.ok. - Интерцепторы:
onRequest,onResponse,onRequestError,onResponseError. - Поддержку BaseURL через
useRequestFetchи контекст SSR.
Сравнение с нативным fetch
// Нативный fetch
const res = await fetch('/api/users')
if (!res.ok) throw new Error('HTTP error')
const data = await res.json()
// $fetch в Nuxt
const data = await $fetch('/api/users')
// data уже распарсен, ошибки выбрасываются автоматически
SSR-специфика: почему не вызывать $fetch напрямую в useAsyncData
Когда компонент рендерится на сервере, вызов $fetch('/api/...') напрямую приводит к HTTP-запросу на тот же сервер — это лишнее сетевое взаимодействие. Вместо этого Nuxt рекомендует использовать useAsyncData + $fetch или просто useFetch:
// Неоптимально на SSR (двойной вызов при гидратации)
const data = await $fetch('/api/posts')
// Правильно: один вызов на сервере, результат передаётся клиенту
const { data } = await useAsyncData('posts', () => $fetch('/api/posts'))
// Или через useFetch (обёртка над useAsyncData + $fetch)
const { data } = await useFetch('/api/posts')
Создание кастомного экземпляра
Для добавления глобальных заголовков (Authorization, Accept-Language) используйте $fetch.create():
// composables/useApiFetch.ts
export const useApiFetch = () => {
const { token } = useAuth()
return $fetch.create({
baseURL: useRuntimeConfig().public.apiBase,
headers: {
Authorization: `Bearer ${token.value}`
},
onResponseError({ response }) {
if (response.status === 401) {
navigateTo('/login')
}
}
})
}
Интерцепторы
const data = await $fetch('/api/data', {
onRequest({ options }) {
options.headers = {
...options.headers,
'X-Request-ID': crypto.randomUUID()
}
},
onResponseError({ response }) {
console.error('API error:', response._data)
}
})
Server-side: useRequestFetch
Внутри server-хуков и middleware Nuxt автоматически передаёт cookie запроса через useRequestFetch(), что необходимо для аутентифицированных server-side вызовов:
// plugins/api.server.ts
export default defineNuxtPlugin(() => {
const fetchWithCookies = useRequestFetch()
return {
provide: {
api: fetchWithCookies
}
}
})
Подводные камни
- Двойной запрос при SSR — использование $fetch вне useAsyncData/useFetch в компоненте приводит к выполнению запроса на сервере и повторно на клиенте при гидратации.
- FetchError.data — при ошибке ответ сервера доступен в
error.data, а не вerror.message; начинающие ловят ошибку и теряют тело ответа. - BaseURL на сервере — $fetch с относительным путём на сервере не знает хост; нужно либо задать baseURL, либо использовать useRequestURL().
- Куки не пробрасываются автоматически — при SSR $fetch не копирует cookie клиента в исходящий запрос; для этого используйте useRequestFetch().
- Отсутствие встроенного кеша — $fetch не кеширует ответы сам по себе; кеширование обеспечивает useAsyncData через ключ.
- Интерцепторы не наследуются — если создали кастомный экземпляр через $fetch.create(), интерцепторы работают только для него, не для глобального $fetch.
- Таймаут не задан по умолчанию — долгий запрос будет висеть без ошибки; явно добавляйте
timeoutв опции.
Common mistakes
- Путать $fetch с похожим API из соседнего фреймворка.
- Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
- Игнорировать влияние на hydration, cache, bundle size или безопасность.
What the interviewer is testing
- Точно объясняет назначение механизма «$fetch».
- Показывает корректный минимальный пример без выдуманных API.
- Называет ограничения, failure modes и production-компромиссы.