Общее понятие JavaScript API в веб-разработке

Раздел: API и интеграции -> API

Основы работы с JavaScript API

JavaScript API обеспечивает взаимодействие веб-приложений с серверными ресурсами. Современный стандарт fetch с async/await является наиболее эффективным и удобным решением для большинства задач.

Как выполнить GET запрос и обработать JSON ответ?

async function fetchData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Ошибка запроса:', error);
    throw error;
  }
}

fetchData('https://api.example.com/users')
  .then(users => console.log(users))
  .catch(err => console.error(err));

Js api (javascript api (общее))

Пояснение: fetch возвращает объект Response. Свойство ok проверяет успешность HTTP статуса (200-299). Метод json() парсит тело ответа как JSON. Ошибки сети (нет соединения) перехватываются в блоке catch.

Типичные ошибки и их решение:

  • Необработанная ошибка сети: всегда добавлять try/catch или .catch()
  • Неверный статус: проверять response.ok, иначе ответ может быть JSON с ошибкой, который не распарсится
  • Проблемы CORS: если сервер не разрешает кросс-доменные запросы, браузер блокирует ответ. Решение: настроить сервер или использовать прокси

Как выполнить асинхронный запрос без использования fetch? (XMLHttpRequest)

function loadData(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(`HTTP error: ${xhr.status}`));
      }
    };
    xhr.onerror = function() {
      reject(new Error('Network error'));
    };
    xhr.send();
  });
}

loadData('https://api.example.com/users')
  .then(data => console.log(data))
  .catch(err => console.error(err));

Text javascript src https apis (apis javascript)

Пояснение: XMLHttpRequest - классический способ, требует обёртки в Promise для удобства. onload срабатывает при получении ответа, onerror - при сетевой ошибке.

Проблемы: более громоздкий код, сложность с обработкой прогресса, отсутствие поддержки промисов из коробки.

Как обработать POST запрос с JSON телом?

async function postData(url, data) {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Server error: ${response.status} - ${errorText}`);
    }
    return await response.json();
  } catch (error) {
    console.error('POST request failed:', error);
    throw error;
  }
}

postData('https://api.example.com/users', { name: 'Иван', age: 30 })
  .then(result => console.log(result));

Важно установить заголовок Content-Type: application/json, иначе сервер может не распарсить тело. При ошибках сервера (4xx/5xx) стоит прочитать тело ответа, так как в нём может быть детальное сообщение.

Распространённая ошибка: забыть преобразовать объект в JSON строку (JSON.stringify) или не указать метод.

Как использовать стороннюю библиотеку Axios для упрощения запросов?

import axios from 'axios';

async function getUsers() {
  try {
    const response = await axios.get('https://api.example.com/users');
    return response.data; // axios автоматически парсит JSON
  } catch (error) {
    if (error.response) {
      console.error('Server responded with status', error.response.status);
    } else if (error.request) {
      console.error('No response received');
    } else {
      console.error('Request setup error', error.message);
    }
    throw error;
  }
}

Axios предоставляет удобные методы (get, post), автоматически преобразует ответ в JSON, обрабатывает таймауты, поддержка отмены запросов и перехватчиков (interceptors).

Недостатки: дополнительная зависимость, увеличение размера бандла. Для простых проектов fetch может быть достаточным.

Как выполнить GraphQL запрос через fetch?

async function graphqlQuery(url, query, variables = {}) {
  const response = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, variables })
  });
  const result = await response.json();
  if (result.errors) {
    throw new Error('GraphQL Error: ' + JSON.stringify(result.errors));
  }
  return result.data;
}

graphqlQuery('https://api.example.com/graphql', `
  query {
    users {
      id
      name
    }
  }
`).then(data => console.log(data));

GraphQL всегда использует POST, даже для запросов (хотя теоретически можно и GET). Ответ содержит поля data и errors.

Как работать с WebSocket API для реального времени?

const socket = new WebSocket('wss://api.example.com/ws');

socket.addEventListener('open', (event) => {
  console.log('Соединение открыто');
  socket.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));
});

socket.addEventListener('message', (event) => {
  const data = JSON.parse(event.data);
  console.log('Получено сообщение:', data);
});

socket.addEventListener('error', (event) => {
  console.error('Ошибка WebSocket:', event);
});

socket.addEventListener('close', (event) => {
  console.log('Соединение закрыто', event.code, event.reason);
});

WebSocket устанавливает постоянное двустороннее соединение. Данные передаются в виде строк или бинарных данных. Сервер может отправлять сообщения в любой момент.

Типичные проблемы: потеря соединения (нужна логика переподключения), обработка разных типов сообщений, корректное закрытие сокета.

Каждый из вариантов имеет свои случаи использования: fetch - для большинства REST запросов, XMLHttpRequest - при работе с устаревшими браузерами или отслеживанием прогресса загрузки, Axios - для сложной конфигурации, GraphQL - для гибких запросов к одному endpoint, WebSocket - для чатов, игр, уведомлений.

Расширенные примеры работы с JavaScript API

Параллельные запросы с Promise.all

Пример
async function fetchMultipleUsers(userIds) {
  const urls = userIds.map(id => `https://api.example.com/users/${id}`);
  const promises = urls.map(url => fetch(url).then(res => res.json()));
  const users = await Promise.all(promises);
  return users;
}

fetchMultipleUsers([1, 2, 3]).then(users => console.log(users));
// Результат:
// [
//   { id: 1, name: 'Анна' },
//   { id: 2, name: 'Борис' },
//   { id: 3, name: 'Виктор' }
// ]

[ { id: 1, name: 'Анна' }, { id: 2, name: 'Борис' }, { id: 3, name: 'Виктор' } ]

Если один из запросов падает, весь Promise.all отклоняется. Для устойчивости можно использовать Promise.allSettled.

Отмена запроса с AbortController

Пример
const controller = new AbortController();
const signal = controller.signal;

fetch('https://api.example.com/long-operation', { signal })
  .then(response => response.json())
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Запрос отменён пользователем');
    } else {
      console.error('Ошибка:', err);
    }
  });

// Через 2 секунды отменяем запрос
setTimeout(() => controller.abort(), 2000);

Полезно для поисковых полей с автодополнением (debounce), чтобы отменять устаревшие запросы.

Загрузка файла с прогрессом через fetch (используя ReadableStream)

Пример
async function uploadFile(url, file) {
  const response = await fetch(url, {
    method: 'POST',
    body: file // FormData или Blob
  });
  // Для отслеживания прогресса загрузки на клиенте fetch не предоставляет встроенных средств,
  // но можно использовать XMLHttpRequest или измерять размер отправленного чанка (сложно).
  // Альтернатива: использовать библиотеку (например, axios с onUploadProgress).
  return response.json();
}
Пример
// Пример с XMLHttpRequest (отслеживание прогресса)
function uploadFileWithProgress(url, file, onProgress) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url);
    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const percent = Math.round((e.loaded / e.total) * 100);
        onProgress(percent);
      }
    });
    xhr.onload = () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error('Upload failed'));
      }
    };
    xhr.onerror = () => reject(new Error('Network error'));
    xhr.send(file);
  });
}

uploadFileWithProgress('/upload', file, (p) => console.log(`${p}%`));

Обработка ошибок разных типов

Пример
async function safeFetch(url) {
  try {
    const response = await fetch(url);
    // Проверка статуса
    if (!response.ok) {
      // Попытка извлечь детали ошибки из тела
      let errorBody;
      try {
        errorBody = await response.json();
      } catch {
        errorBody = await response.text();
      }
      throw new Error(`HTTP ${response.status}: ${JSON.stringify(errorBody)}`);
    }
    return await response.json();
  } catch (error) {
    if (error instanceof TypeError && error.message.includes('fetch')) {
      // Ошибка сети, CORS, неверный URL
      console.error('Сетевая ошибка или CORS:', error);
    }
    throw error;
  }
}

Такой подход позволяет единообразно обрабатывать ошибки как на стороне сети, так и на стороне сервера.

Работа с куками и авторизацией

Пример
fetch('https://api.example.com/protected', {
  method: 'GET',
  credentials: 'include', // отправка кук (для same-origin и cross-origin с CORS)
  headers: {
    'Authorization': 'Bearer ' + getToken()
  }
});

Параметр credentials: 'include' обязателен, если сервер ожидает куки или сессию. Для кросс-доменов сервер должен установить заголовок Access-Control-Allow-Credentials: true.

Полиморфный запрос: обработка разных типов ответов

Пример
async function fetchWithType(url) {
  const response = await fetch(url);
  const contentType = response.headers.get('content-type');
  if (contentType && contentType.includes('application/json')) {
    return await response.json();
  } else if (contentType && contentType.includes('text/')) {
    return await response.text();
  } else if (contentType && contentType.includes('image/')) {
    // Для изображений можно получить blob и создать URL
    const blob = await response.blob();
    return URL.createObjectURL(blob);
  } else if (contentType && contentType.includes('application/octet-stream')) {
    const arrayBuffer = await response.arrayBuffer();
    return arrayBuffer;
  } else {
    return await response.text();
  }
}

Применяется при работе с API, которые могут возвращать разные форматы (например, JSON при успехе и HTML при ошибке).

Повторная попытка при временных ошибках (retry)

Пример
async function fetchWithRetry(url, options = {}, retries = 3, delay = 1000) {
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      const response = await fetch(url, options);
      if (!response.ok && response.status >= 500) {
        // Серверная ошибка, стоит повторить
        throw new Error(`Server error ${response.status}`);
      }
      return await response.json();
    } catch (error) {
      if (attempt === retries) throw error;
      console.warn(`Попытка ${attempt + 1} не удалась, повтор через ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
      delay *= 2; // экспоненциальная задержка
    }
  }
}

Полезно для API с временной недоступностью. Не применять к идемпотентным запросам (GET) без осторожности.

JavaScript API (общее) - comments

En
Js api (javascript)