Fetch API в Vue приложениях

Fetch API — стандартный веб-интерфейс для выполнения HTTP-запросов, встроенный в современные браузеры. В контексте Vue-приложений он используется для получения данных с серверов, отправки форм, взаимодействия с REST API и микросервисами. В отличие от сторонних библиотек, Fetch не требует установки зависимостей и полностью основан на промисах.


Базовый синтаксис Fetch API

Fetch вызывается через глобальную функцию fetch, принимающую URL и объект настроек:

fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => {
    console.log(data)
  })
  .catch(error => {
    console.error(error)
  })

Ключевые особенности:

  • fetch не отклоняет промис при HTTP-ошибках (404, 500)

  • для обработки тела ответа используются методы:

    • response.json()
    • response.text()
    • response.blob()
  • сетевые ошибки и CORS-ошибки приводят к catch


Использование Fetch внутри Vue-компонента

Во Vue Fetch обычно вызывается в методах или в жизненных циклах компонента.

Пример с Options API

export default {
  data() {
    return {
      users: [],
      loading: false,
      error: null
    }
  },
  mounted() {
    this.loadUsers()
  },
  methods: {
    async loadUsers() {
      this.loading = true
      this.error = null

      try {
        const response = await fetch('https://api.example.com/users')

        if (!response.ok) {
          throw new Error(`Ошибка ${response.status}`)
        }

        this.users = await response.json()
      } catch (e) {
        this.error = e.message
      } finally {
        this.loading = false
      }
    }
  }
}

Здесь используется async/await, что делает код линейным и более читаемым.


Fetch и Composition API

В Composition API Fetch чаще всего используется внутри setup или вынесен в отдельные composable-функции.

import { ref, onMounted } from 'vue'

export default {
  setup() {
    const users = ref([])
    const loading = ref(false)
    const error = ref(null)

    const loadUsers = async () => {
      loading.value = true

      try {
        const res = await fetch('https://api.example.com/users')
        if (!res.ok) throw new Error('Ошибка загрузки')
        users.value = await res.json()
      } catch (e) {
        error.value = e.message
      } finally {
        loading.value = false
      }
    }

    onMounted(loadUsers)

    return { users, loading, error }
  }
}

Отправка данных на сервер (POST, PUT, DELETE)

Fetch поддерживает любые HTTP-методы через параметр method.

POST-запрос с JSON-телом

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Ivan',
    email: 'ivan@example.com'
  })
})

Основные параметры конфигурации:

  • method — HTTP-метод
  • headers — заголовки запроса
  • body — тело запроса (строка, FormData, Blob)

Работа с формами и FormData

Для отправки файлов и сложных форм используется FormData.

const formData = new FormData()
formData.append('avatar', file)
formData.append('name', 'Ivan')

fetch('/upload', {
  method: 'POST',
  body: formData
})

При использовании FormData заголовок Content-Type не указывается вручную — браузер установит его автоматически.


Обработка ошибок HTTP

Fetch считает ошибкой только проблемы сети. Проверка статуса выполняется вручную:

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`)
}

Часто используется вспомогательная функция:

async function fetchJson(url, options = {}) {
  const res = await fetch(url, options)
  if (!res.ok) {
    const message = await res.text()
    throw new Error(message)
  }
  return res.json()
}

Вынос Fetch-логики в сервисы

Для поддержки масштабируемости Fetch-запросы выносятся в отдельные модули.

// api/users.js
export async function getUsers() {
  const res = await fetch('/api/users')
  if (!res.ok) throw new Error('Ошибка')
  return res.json()
}

Использование в компоненте:

import { getUsers } from '@/api/users'

const users = await getUsers()

Преимущества подхода:

  • повторное использование логики
  • централизованная обработка ошибок
  • упрощение компонентов

Авторизация и заголовки

Для передачи токенов используется заголовок Authorization.

fetch('/api/profile', {
  headers: {
    Authorization: `Bearer ${token}`
  }
})

При использовании JWT токен обычно хранится:

  • в localStorage
  • в sessionStorage
  • в состоянии Vuex / Pinia

Fetch и CORS

Fetch подчиняется политике CORS браузера. Основные режимы:

fetch(url, {
  mode: 'cors'        // по умолчанию
  // mode: 'no-cors'
})

Ограничения:

  • без корректных CORS-заголовков сервером данные недоступны
  • no-cors не позволяет читать тело ответа

Прерывание запроса с AbortController

Для отмены запросов (например, при смене маршрута) используется AbortController.

const controller = new AbortController()

fetch(url, {
  signal: controller.signal
})

controller.abort()

Во Vue это применяется при:

  • быстром вводе (debounce)
  • переходе между страницами
  • обновлении данных

Реактивная загрузка данных

Fetch часто комбинируется с watch или watchEffect.

watch(searchQuery, async (value) => {
  const res = await fetch(`/api/search?q=${value}`)
  results.value = await res.json()
})

Это позволяет автоматически выполнять запросы при изменении состояния.


Ограничения Fetch API

  • отсутствие таймаута по умолчанию
  • ручная обработка ошибок
  • отсутствие интерцепторов
  • нет автоматической сериализации

По этим причинам в крупных проектах Fetch часто оборачивается в собственные абстракции или заменяется библиотеками, но знание Fetch остаётся фундаментальным, так как он лежит в основе большинства HTTP-решений в JavaScript.


Типичные архитектурные схемы использования Fetch во Vue

  • Компонент → API-сервис → Fetch
  • Composable → Fetch
  • Store (Pinia) → Fetch → Компоненты

Fetch при этом остаётся низкоуровневым инструментом, отвечающим исключительно за сетевое взаимодействие, без привязки к UI или состоянию.