Введение:
API-интерфейс Fetch в JavaScript — мощный инструмент для выполнения сетевых запросов, но обработка ошибок и данных ответов может оказаться сложной задачей. В этой статье мы рассмотрим различные подходы к улучшению обработки ошибок и усовершенствованию Fetch API для более эффективного и надежного извлечения данных.

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

{
  const res = await fetch('/user');
  const user = await res.json();
}

Хотя этот подход прост, у него есть несколько проблем, в первую очередь связанных с обработкой ошибок.

Обработка ошибок
Для обработки ошибок мы можем использовать блоки try-catch:

try {
 const res = await fetch('/user');
 const user = await res.json();
} catch (err) {
 // Handle the error
}

Однако этот подход все еще оставляет возможности для совершенствования, поскольку предполагает успешный ответ без учета потенциальных кодов состояния, отличных от 200.

Более безопасный подход
Более безопасный подход предполагает проверку кода состояния ответа:

try {
  const res = await fetch('/user');

  if (!res.ok) {
    switch (res.status) {
      case 400: /* Handle */ break;
      case 401: /* Handle */ break;
      case 404: /* Handle */ break;
      case 500: /* Handle */ break;
    }
  }

  const user = await res.json();
} catch (err) {
  // Handle the error
}

Этот подход обеспечивает лучшую обработку ошибок, но может стать громоздким и менее читабельным, особенно при работе с несколькими конечными точками и кодами состояния.

Более элегантное решение
Более элегантное решение — создать пользовательскую ошибку с подробностями ответа:

class ResponseError extends Error {
  constructor(message, res) {
    super(message);
    this.response = res;
  }
}

try {
  const res = await fetch('/user');

  if (!res.ok) {
    throw new ResponseError('Bad fetch response', res);
  }

  const user = await res.json();
} catch (err) {
  // Handle the error with access to status and body
  switch (err.response.status) {
    case 400: /* Handle */ break;
    case 401: /* Handle */ break;
    case 404: /* Handle */ break;
    case 500: /* Handle */ break;
  }
}

Этот подход позволяет более разумно обрабатывать ошибки на основе кодов состояния и обеспечивает более информативные журналы.

Создание оболочки
Чтобы упростить обработку ошибок в проекте, рассмотрите возможность создания собственной оболочки Fetch:

class ResponseError extends Error {
  constructor(message, res) {
    super(message);
    this.response = res;
  }
}

export async function myFetch(...options) {
  const res = await fetch(...options);
  if (!res.ok) {
    throw new ResponseError('Bad fetch response', res);
  }
  return res;
}

Затем вы можете использовать его следующим образом:

try {
  const res = await myFetch('/user');
  const user = await res.json();
} catch (err) {
  // Handle issues via error.response.*
}

Эта оболочка обеспечивает согласованность и упрощает обработку ошибок в рамках всего проекта.

Решения с открытым исходным кодом
Рассмотрите возможность использования популярных альтернатив с открытым исходным кодом, таких как Axios, Redaxios или Wretch, которые обрабатывают сценарии ошибок и упрощают получение данных:

Axios и Redaxios обеспечивают автоматическую обработку ошибок и привычное использование API.
Redaxios — это облегченная альтернатива Axios для пакетов меньшего размера.
Wretch предлагает интерфейс, похожий на выборку, с дополнительными методами для обработки общих статусов.

Безопасная обработка данных
При отправке данных через запросы POST, PUT или PATCH убедитесь, что вы правильно сериализуете тело и устанавливаете заголовок Content-Type:

const res = await fetch('/user', {
  method: 'POST',
  body: JSON.stringify({ name: 'Zamin Mirzad', company: 'Medium' }),
  headers: {
    'Content-Type': 'application/json'
  }
});

Это обеспечивает безопасную передачу данных и правильную обработку на сервере.

Обеспечение типобезопасности оболочки (необязательно)
Чтобы обеспечить типобезопасность пользовательской оболочки выборки с помощью TypeScript, добавьте аннотации типов и соответствующим образом обрабатывайте ошибки:

export async function myFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
  // ...
}

try {
  const res = await myFetch('/user');
  const user = await res.json();
} catch (err: unknown) {
  if (err instanceof ResponseError) {
    // Handle known errors
  } else {
    // Handle unexpected errors
  }
}

Это обеспечивает правильную обработку ошибок и поддерживает безопасность типов.

Многократная обработка ошибок
Рассмотрите возможность инкапсуляции общей логики обработки ошибок в функцию многократного использования для обработки единичных случаев и обработки по умолчанию:

try {
  const res = await myFetch('/user');
  const user = await res.json();
} catch (err) {
  if (err instanceof ResponseError) {
    if (err.response.status === 404) {
      // Handle specific case
      return;
    }
  }
  // Default error handling
  handleError(err);
}

Такой подход централизует обработку ошибок и обеспечивает согласованность.

Заключение
Независимо от того, решите ли вы использовать специальную оболочку выборки, решение с открытым исходным кодом или их комбинацию, правильная обработка ошибок и передача данных имеют решающее значение для надежной веб-разработки. Выберите подход, который лучше всего соответствует требованиям вашего проекта, и сосредоточьтесь на безопасном и эффективном получении данных.