Перейти к основному содержимому

Fetch / axios — типовые запросы


Для кого эта статья

Подборка готовых HTTP-запросов на JavaScript с разбором «что написано» и «зачем так» — как в галерее Turtle и Python — файлы и текст. Материал рассчитан на:

  • школьников — информатика, кружок, первый сайт с данными с сервера;
  • студентов — лабораторная «REST API», курсовой фронтенд, React/Vue;
  • самоучек — когда в Google ищут «javascript fetch example», «axios get post», «fetch api json», «как отправить post запрос javascript»;
  • тех, кто уже проверил API через curl и хочет тот же запрос в коде.

Каждый пример можно скопировать и сразу запустить — регистрация на учебных API не нужна.

Сначала теория

Проверка API в терминале — curl / fetch — API-запросы. Promise и async/await — асинхронное программирование. Каркас API-клиента — практика JavaScript. Отмена запросов — AbortController. Тот же GET в мобильном приложении — Flutter + FutureBuilder.


Как запустить любой пример

Вариант 1 — консоль браузера (самый быстрый)

  1. Откройте любой сайт или пустую вкладку.
  2. Нажмите F12 → вкладка Console.
  3. Вставьте код с await — в современных браузерах top-level await в консоли разрешён.
  4. Нажмите Enter. Ответ появится в консоли или во вкладке Network.

Если браузер ругается на await снаружи функции, оберните код:

(async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
console.log(await res.json());
})();

Разбор обёртки:

ЧастьСмысл
(async () => { ... })()Создали анонимную async-функцию и сразу вызвали
Зачемawait работает только внутри async-функции
console.log(await res.json())Печатаем разобранный JSON в консоль

Вариант 2 — Node.js 18+

  1. Установите Node.js (LTS).
  2. Создайте файл request.mjs (расширение .mjs — чтобы работал import).
  3. Вставьте пример, сохраните.
  4. В терминале в папке с файлом:
node request.mjs

Вариант 3 — проект Vite / React

  1. npm create vite@latest my-app → выберите React или Vanilla.
  2. cd my-app && npm install — для axios ещё npm install axios.
  3. Код fetch/axios — в useEffect, обработчик кнопки или отдельный файл src/api/....

Учебные URL бесплатны: jsonplaceholder.typicode.com, httpbin.org.


Что такое fetch и axios — простыми словами

Сайт в браузере обычно запрашивает у сервера данные по HTTP — как вы открываете страницу, только ответ приходит JSON (текст с { "ключ": "значение" }), а не готовая HTML-страница.

СловоПростое объяснение
fetchВстроенная функция браузера и Node.js — «сходи по URL, принеси ответ»
axiosБиблиотека npm — то же HTTP, но короче код и удобнее ошибки
GET«Дай прочитать» — список постов, профиль пользователя
POST«Прими новые данные» — форма, создание записи
JSONФормат обмена: {"title":"hello","userId":1}
Promise«Обещание результата потом» — поэтому нужен await
401 / 404Код ответа: нет доступа / не найдено

Частые запросы в Google — куда смотреть

Ищут в интернетеРаздел ниже
javascript fetch example / fetch api примерОбязательный шаблон fetch
fetch get json / как получить json fetchGET — один пост
fetch post json / javascript post requestPOST с JSON
axios get request example / axios get примерОбязательный шаблон axios
axios post example / axios post jsonPOST с JSON
fetch query parameters / параметры в urlQuery-параметры
axios bearer token / authorization headerBearer-токен
fetch timeout / abortcontroller fetchТаймаут
axios interceptors / axios create instanceInterceptors
react fetch useeffect / загрузка данных reactReact useEffect
fetch vs axios / чем отличается axiosfetch и axios — когда что
failed to fetch cors / cors errorCORS
javascript api request localhostlocalhost

Основы — с чего начать

Обязательный шаблон fetch

Любой рабочий GET на fetch строится из трёх шагов:

  1. fetch(url) — отправить запрос.
  2. Проверить res.ok — убедиться, что сервер не вернул 404/500.
  3. await res.json() — прочитать тело как JSON.

Задача: получить заголовок поста с id=1 и вывести в консоль.

const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');

if (!res.ok) {
throw new Error(`HTTP ${res.status} ${res.statusText}`);
}

const data = await res.json();
console.log(data.title);

Разбор построчно:

СтрокаЧто происходитЗачем
const res = await fetch('...')Браузер отправляет GET на URL и ждёт ответfetch возвращает Promise; await «останавливает» код до ответа
URL в кавычкахАдрес ресурса на сервере/posts/1 — пост с номером 1
if (!res.ok)Проверка: статус не из диапазона 200–299fetch не считает 404 ошибкой JavaScript — только res.ok === false
throw new Error(...)Прерывает выполнение с текстом ошибкиВ консоли красное сообщение HTTP 404 Not Found
`HTTP ${res.status}`Шаблонная строка — подставляет число статусаУдобно для отладки и отчёта
const data = await res.json()Читает тело ответа и парсит JSON → объект JSВторой await: чтение потока тоже асинхронное
console.log(data.title)Печатает поле title из объектаТак данные попадают на экран или в отладку

Что увидите в консоли (фрагмент):

sunt aut facere repellat provident occaecati excepturi optio reprehenderit

Частая ошибка: забыть await перед res.json() — в переменной окажется Promise, а не объект, и data.title будет undefined.

Попробуйте: URL .../posts/99999 — сработает !res.ok, увидите HTTP 404.


Обязательный шаблон axios

axios — отдельная библиотека. Её ставят через npm, JSON разбирается автоматически, HTTP-ошибки бросают исключение.

Установка (один раз в проекте):

npm install axios

Задача: тот же GET — заголовок поста id=1.

import axios from 'axios';

const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
console.log(data.title);

Разбор построчно:

СтрокаЧто происходитЗачем
import axios from 'axios'Подключает модуль из node_modulesВ браузере через Vite/Webpack; в Node — файл .mjs или "type":"module"
axios.get(url)GET-запрос; внутри — тот же HTTP, что у fetchКороткая запись вместо fetch + опций
const { data } = await ...Деструктуризация — достаём поле data из ответа axiosaxios всегда кладёт тело ответа в .data
data.titleПоле объекта, как у fetchСтруктура JSON от сервера та же

Что увидите: тот же заголовок поста, что и в примере с fetch.

Разница с fetch при ошибке:

try {
await axios.get('https://jsonplaceholder.typicode.com/posts/99999');
} catch (error) {
console.log('Статус:', error.response?.status); // 404
}
fetchaxios
404res.ok === false, код не падаетcatch, error.response.status === 404
JSONawait res.json() вручнуюуже в data

Попробуйте: запустите блок try/catch выше — увидите статус 404 без ручной проверки ok.


fetch и axios — когда что

Критерийfetchaxios
УстановкаУже есть в браузере и Node 18+npm install axios
Строк кода на GET~5 с проверкой ok~2
Ошибка 404/500Проверка res.okАвтоматически throw
ТаймаутAbortController (~10 строк)timeout: 5000
Токен на все запросыСвоя функция apiRequestinterceptors
Курсовая / лабораторнаяОтлично — без зависимостейОтлично — меньше boilerplate
Большой React-проектНужна обёрткаЧастый выбор команды

Для первого знакомства начните с fetch в консоли F12. Когда появится проект с package.json — добавьте axios.


Стартовые запросы

GET — один пост по id

Задача: прочитать один ресурс по номеру — самый частый запрос в учебниках («получить пользователя», «получить товар»).

fetch:

const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const post = await res.json();
console.log(post.id, post.title);

axios:

import axios from 'axios';

const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
console.log(post.id, post.title);

Разбор полей ответа:

ПолеПримерСмысл
id1Номер записи в базе
titleстрокаЗаголовок поста
bodyдлинный текстТекст поста
userId1Автор — связь с другим ресурсом /users/1

Что увидите в консоли:

1 sunt aut facere repellat provident occaecati excepturi optio reprehenderit

Как проверить в DevTools: F12 → Network → обновите или выполните код → клик по строке posts/1 → вкладки Headers (статус 200) и Response (весь JSON).

Попробуйте: замените 1 на 99999 — fetch: res.ok === false; axios: ошибка в catch.


POST с JSON

Задача: отправить новый объект на сервер — кнопка «Сохранить», форма регистрации, создание заметки.

fetch:

const res = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'hello',
body: 'text',
userId: 1,
}),
});

if (!res.ok) throw new Error(`HTTP ${res.status}`);
const created = await res.json();
console.log('Новый id:', created.id);

Разбор построчно (fetch):

СтрокаЧто происходитЗачем
второй аргумент { ... }Настройки запросаБез него fetch делает только GET
method: 'POST'HTTP-метод «создать / отправить»GET тело обычно не несёт
headers: { 'Content-Type': 'application/json' }Говорим серверу: «в теле JSON»Без заголовка сервер может не понять формат
JSON.stringify({...})Объект JS → строка для HTTP-телаПо сети летит текст, не «живой» объект
title, body, userIdПоля, которые ждёт jsonplaceholderУ вашего API список полей будет в документации
created.idСервер «притворяется», что создал запись и вернул idУчебный API не сохраняет навсегда — id всё равно приходит в ответе

axios:

import axios from 'axios';

const { data: created } = await axios.post(
'https://jsonplaceholder.typicode.com/posts',
{ title: 'hello', body: 'text', userId: 1 },
);
console.log('Новый id:', created.id);

Разбор: axios сам ставит Content-Type: application/json и вызывает JSON.stringify за вас — вторым аргументом .post() передаётся обычный объект.

Что увидите:

Новый id: 101

(число может отличаться — jsonplaceholder возвращает фиктивный id.)

Частая ошибка: передать объект в body без JSON.stringify в fetch — сервер получит [object Object] и ответит 400 Bad Request.

Попробуйте: во вкладке Network откройте запрос postsPayload — там ваш JSON.


Типовые запросы — углубление

1. Query-параметры

Задача: GET с фильтром — «все посты пользователя 1» (в URL после ?).

fetch:

const params = new URLSearchParams({ userId: '1' });
const url = `https://jsonplaceholder.typicode.com/posts?${params}`;

const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const posts = await res.json();
console.log('Количество:', posts.length);

Разбор построчно:

СтрокаЧто происходитЗачем
new URLSearchParams({ userId: '1' })Строит строку параметровАвтоматически кодирует пробелы и спецсимволы
`...?${params}`Склеивает базовый URL и userId=1Итог: .../posts?userId=1
posts.lengthДлина массиваОтвет — список, не один объект

axios:

import axios from 'axios';

const { data: posts } = await axios.get('https://jsonplaceholder.typicode.com/posts', {
params: { userId: 1 },
});
console.log('Количество:', posts.length);

Разбор: ключ params — axios сам добавит ?userId=1 к адресу. Число 1 можно писать без кавычек.

Что увидите: Количество: 10 (у userId=1 десять постов на jsonplaceholder).

Попробуйте: { params: { userId: 1, _limit: 3 } } — вернётся только 3 поста.


2. PUT, PATCH и DELETE

Задача: для отчёта показать все методы REST на одном URL /posts/1.

МетодСмысл простыми словамиТело запроса
GETПрочитатьОбычно пусто
POSTСоздать новыйНовый объект
PUTЗаменить целикомПолный объект со всеми полями
PATCHИзменить частьТолько изменённые поля
DELETEУдалитьОбычно пусто

fetch — PUT:

const res = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: 1,
title: 'new title',
body: 'new body',
userId: 1,
}),
});
const updated = await res.json();
console.log(updated.title);

Разбор PUT: сервер ожидает все поля ресурса. Отправили только title — в строгих API остальное может обнулиться.

fetch — PATCH:

const res = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'only title changed' }),
});
const patched = await res.json();

Разбор PATCH: «заплатка» — меняем одно поле, остальное сервер не трогает.

fetch — DELETE:

const res = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'DELETE',
});
console.log('Статус:', res.status);

Разбор DELETE: тела обычно нет; успех — часто статус 200 или 204 (без тела).

axios — все три метода:

import axios from 'axios';

const BASE = 'https://jsonplaceholder.typicode.com/posts/1';

await axios.put(BASE, { id: 1, title: 'new', body: 'new', userId: 1 });
await axios.patch(BASE, { title: 'only title changed' });
await axios.delete(BASE);

Частая ошибка в лабораторных: перепутать PUT и PATCH. В отчёте напишите: PUT — полная замена, PATCH — частичное изменение.


3. Bearer-токен

Задача: передать секрет в заголовке — так работают JWT, API keys, «личный кабинет».

fetch:

const TOKEN = 'your-token-here';

const res = await fetch('https://httpbin.org/bearer', {
headers: {
Authorization: `Bearer ${TOKEN}`,
},
});
const data = await res.json();
console.log(data);

Разбор построчно:

СтрокаЧто происходитЗачем
const TOKEN = '...'Переменная с секретомВ реальном проекте — из .env, не из Git
headers: { Authorization: ... }HTTP-заголовок «кто вы»Сервер читает его до тела запроса
`Bearer ${TOKEN}`Формат: слово Bearer, пробел, токенСтандарт OAuth2 / JWT
httpbin /bearerУчебный сервер «эхо»В ответе покажет, какой токен дошёл

axios:

import axios from 'axios';

const TOKEN = 'your-token-here';

const { data } = await axios.get('https://httpbin.org/bearer', {
headers: { Authorization: `Bearer ${TOKEN}` },
});
console.log(data);

Попробуйте: TOKEN = 'test123' — в JSON ответа найдите "test123".

Токены в Git

Никогда не коммитьте реальные ключи в репозиторий. В отчёте пишите: «токен берётся из переменной окружения process.env.API_TOKEN».


4. Форма и загрузка файла

Задача: отправить поля как HTML-форма (логин/пароль) или файл — multipart/form-data.

fetch — FormData:

const form = new FormData();
form.append('login', 'user');
form.append('password', 'pass');
// form.append('avatar', fileInput.files[0]); // раскомментируйте с <input type="file">

const res = await fetch('https://httpbin.org/post', {
method: 'POST',
body: form,
});
const echo = await res.json();
console.log(echo.form);

Разбор построчно:

СтрокаЧто происходитЗачем
new FormData()Контейнер «поля формы»Как виртуальная <form>
form.append('login', 'user')Пара имя=значениеИмя login увидит сервер
нет Content-TypeБраузер сам поставит multipart/form-data с boundaryЕсли поставить application/json вручную — сломается
echo.formhttpbin возвращает зеркало ваших полейПроверка «дошло ли»

axios:

import axios from 'axios';

const form = new FormData();
form.append('login', 'user');
form.append('password', 'pass');

const { data } = await axios.post('https://httpbin.org/post', form);
console.log(data.form);

Что увидите:

{ login: 'user', password: 'pass' }

(в консоли объект с вашими полями.)


5. Таймаут

Задача: сервер «думает» слишком долго — оборвать запрос через 5 секунд, чтобы интерфейс не завис.

fetch + AbortController:

async function fetchJson(url, { timeoutMs = 5000 } = {}) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);

try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} finally {
clearTimeout(timer);
}
}

const post = await fetchJson('https://jsonplaceholder.typicode.com/posts/1');
console.log(post.title);

Разбор построчно:

СтрокаЧто происходитЗачем
async function fetchJson(...)Переиспользуемая функцияОдин раз написали — вызываете из всего проекта
&#123; timeoutMs = 5000 &#125; = &#123;&#125;Параметр по умолчанию 5 секМожно передать &#123; timeoutMs: 10000 &#125;
new AbortController()Объект «кнопка отмены»Стандарт браузера для прерывания fetch
setTimeout(() => controller.abort(), ...)Через N мс нажать «отмена»Если ответ не успел — запрос обрывается
signal: controller.signalСвязь fetch с контроллеромБез signal abort не сработает
finally &#123; clearTimeout(timer) &#125;Убрать таймер, если ответ пришёл раньшеИначе таймеры копятся и «стреляют» позже

axios:

import axios from 'axios';

const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', {
timeout: 5000,
});
console.log(data.title);

Разбор: одна строка timeout: 5000 — то же поведение, что ~15 строк с AbortController.

Попробуйте: fetchJson('https://httpbin.org/delay/5', &#123; timeoutMs: 1000 &#125;) — через 1 с в консоли AbortError.


6. Retry при сбое сервера

Задача: сервер на секунду «лежит» (502/503) — повторить запрос 2–3 раза с паузой.

async function fetchWithRetry(url, { attempts = 3, baseMs = 300 } = {}) {
let lastError;
for (let i = 0; i < attempts; i++) {
try {
const res = await fetch(url);
if (res.status >= 500) throw new Error(`HTTP ${res.status}`);
if (!res.ok) throw new Error(`HTTP ${res.status} (no retry)`);
return await res.json();
} catch (e) {
lastError = e;
if (String(e.message).includes('no retry')) throw e;
if (i === attempts - 1) break;
await new Promise((r) => setTimeout(r, baseMs * 2 ** i));
}
}
throw lastError;
}

Разбор логики:

ШагСмысл
for (let i = 0; i < attempts; i++)До 3 попыток
status >= 500Вина сервера — можно повторить
(no retry) для 400/401/404Ошибка клиента — повтор бессмысленен
baseMs * 2 &#42;&#42; iПауза растёт: 300 мс → 600 → 1200
await new Promise((r) => setTimeout(r, ...))«Подождать» без блокировки вкладки

Попробуйте: URL https://httpbin.org/status/503 и attempts: 2 — в Network две попытки.


7. Interceptors в axios

Задача: один раз прописать токен и обработку 401 — для всех запросов приложения.

import axios from 'axios';

const api = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 10000,
});

api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});

api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
console.warn('Сессия истекла — перенаправление на login');
}
return Promise.reject(error);
},
);

const { data: posts } = await api.get('/posts', { params: { userId: 1 } });
console.log(posts.length);

Разбор построчно:

СтрокаЧто происходитЗачем
axios.create(&#123; baseURL &#125;)Отдельный «клиент» с общим префиксом URL.get('/posts')https://jsonplaceholder.../posts
interceptors.request.useФункция перед каждым запросомПодставить токен, лог, id запроса
localStorage.getItem('token')Читает сохранённый токен браузераПосле логина токен кладут в localStorage
interceptors.response.useДва колбэка: успех и ошибкаНа 401 — выкинуть на страницу входа
Promise.reject(error)Пробросить ошибку дальшеЧтобы catch в компоненте тоже сработал

fetch-аналог — одна обёртка (без встроенных interceptors):

const API_BASE = 'https://jsonplaceholder.typicode.com';

async function apiRequest(path, options = {}) {
const token = localStorage.getItem('token');
const res = await fetch(`${API_BASE}${path}`, {
method: options.method ?? 'GET',
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...(options.headers ?? {}),
},
body: options.body ? JSON.stringify(options.body) : undefined,
signal: options.signal,
});
if (!res.ok) throw new Error(`HTTP ${res.status} for ${path}`);
return res.json();
}

Разбор ... (spread): если token есть — в заголовки добавится Authorization; если нет — поле не попадёт в объект.


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

Задача: загрузить пост и комментарии одновременно — быстрее, чем два раза подряд.

fetch:

const [postRes, commentsRes] = await Promise.all([
fetch('https://jsonplaceholder.typicode.com/posts/1'),
fetch('https://jsonplaceholder.typicode.com/posts/1/comments'),
]);

if (!postRes.ok || !commentsRes.ok) {
throw new Error('Один из запросов завершился ошибкой');
}

const [post, comments] = await Promise.all([
postRes.json(),
commentsRes.json(),
]);

console.log(post.title, 'комментариев:', comments.length);

Разбор:

СтрокаСмысл
Promise.all([...])Запускает оба fetch параллельно
Первый Promise.allЖдёт HTTP-ответы (заголовки)
Второй Promise.allПараллельно читает тела JSON
comments.lengthКомментарии — массив

axios:

import axios from 'axios';

const [postRes, commentsRes] = await Promise.all([
axios.get('https://jsonplaceholder.typicode.com/posts/1'),
axios.get('https://jsonplaceholder.typicode.com/posts/1/comments'),
]);

console.log(postRes.data.title, 'комментариев:', commentsRes.data.length);

Что увидите: заголовок поста и число комментариев (обычно 5).


9. React — загрузка в useEffect

Задача: открыли страницу /post/1 → показали «Загрузка…» → заголовок или текст ошибки.

import { useEffect, useState } from 'react';

function PostPage({ id }) {
const [post, setPost] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const controller = new AbortController();

async function load() {
setLoading(true);
setError(null);
try {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${id}`,
{ signal: controller.signal },
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
setPost(await res.json());
} catch (e) {
if (e.name !== 'AbortError') setError(e.message);
} finally {
setLoading(false);
}
}

load();
return () => controller.abort();
}, [id]);

if (loading) return <p>Загрузка…</p>;
if (error) return <p>Ошибка: {error}</p>;
return <h1>{post.title}</h1>;
}

Разбор построчно:

СтрокаЧто происходитЗачем
useState(null)Три «ячейки памяти» компонентаpost, loading, error — стандартный набор для API
useEffect(..., [id])Запуск при монтировании и при смене idПерешли с /post/1 на /post/2 — новый запрос
async function load() внутри effectfetch нельзя передать «голым» async в useEffectОбёртка — обычный паттерн React
setLoading(true)Показать спиннерПользователь видит, что идёт загрузка
&#123; signal: controller.signal &#125;Привязка к AbortControllerСтарый запрос отменится при смене id
return () => controller.abort()Cleanup при размонтированииУшли со страницы — не обновлять state «в пустоту»
e.name !== 'AbortError'Отмена — не ошибка для пользователяИначе мелькнёт «Ошибка: AbortError»
if (loading) return ...Условный рендерТри экрана: загрузка / ошибка / данные

Сценарий «гонка»: пользователь быстро кликнул post/1 → post/2. Без abort() ответ от «1» может прийти после «2» и перезаписать экран. AbortController это предотвращает.


10. CORS — «Failed to fetch»

Задача: в Postman и curl всё работает, в React — красное Failed to fetch. Почему?

Правило браузера: JavaScript не может прочитать ответ с другого домена, если сервер не разрешил заголовком Access-Control-Allow-Origin.

ИнструментПроверяет CORS?
curlНет
PostmanНет
Node.js fetchНет
БраузерДа

Алгоритм отладки:

  1. Тот же URL в curlпримеры. Если там 200 — бэкенд жив.
  2. F12 → Network — запрос мог уйти, статус 200, но консоль красная.
  3. Решение: настроить CORS на сервере или dev-proxy в Vite:
// vite.config.js — пример
export default {
server: {
proxy: {
'/api': 'http://127.0.0.1:8080',
},
},
};

Разбор proxy: браузер стучится на тот же origin (localhost:5173/api/...), Vite пересылает на бэкенд — CORS не мешает.


11. localhost — свой API на компьютере

Задача: бэкенд крутится у вас на http://127.0.0.1:8080 — проверить из JS.

const res = await fetch('http://127.0.0.1:8080/api/health');
console.log('Статус:', res.status);
console.log('Тело:', await res.text());

Разбор:

СтрокаСмысл
127.0.0.1«Этот компьютер» — loopback
:8080Порт — должен совпадать с тем, что слушает сервер
await res.text()Если ответ не JSON, а plain text «OK»

Перед запуском: сервер уже должен работать (npm run dev, uvicorn, dotnet run…). Иначе Failed to fetch — «ничего не слушает порт».


Переиспользуемые базы

Структура папок API-слоя

src/
api/
http.js # fetchJson или axios.create
posts.js # getPost, createPost
users.js # getUser
// api/posts.js
import { apiRequest } from './http.js';

export function getPost(id) {
return apiRequest(`/posts/${id}`);
}

export function createPost(body) {
return apiRequest('/posts', { method: 'POST', body });
}

Зачем так делать:

  • компоненты React не знают полный URL — только getPost(1);
  • токен и обработка ошибок — в одном файле http.js;
  • в лабораторной легко показать: «слой API отделён от UI».

Частые вопросы (коротко)

Почему fetch не падает на 404?
Сеть ответила — Promise выполнился. HTTP 404 — это «ответ с ошибкой», не обрыв связи. Проверяйте res.ok.

Зачем два await подряд?
Первый — ждём заголовки HTTP. Второй — читаем тело (JSON). Оба шага асинхронные.

Как отправить POST с JSON?
fetch: method: 'POST', заголовок Content-Type: application/json, body: JSON.stringify(obj). axios: axios.post(url, obj).

fetch или axios для курсовой?
Оба зачтут. fetch — меньше зависимостей; axios — меньше строк и встроенный timeout.

Где хранить API key?
Секреты — на сервере. В клиентском JS ключ виден любому в DevTools.

Что такое res.json() vs res.text()?
.json() — парсит {...} в объект. .text() — сырая строка (HTML, plain text).


Типичные ошибки

СимптомЧастая причинаЧто сделать
Failed to fetchCORS, сервер выключен, mixed HTTP/HTTPScurl; проверить порт; proxy в Vite
Unexpected token < in JSONСервер вернул HTML-страницу ошибкиres.text() и посмотреть; проверить URL
401 UnauthorizedНет заголовка AuthorizationBearer $&#123;token&#125;
data is undefinedЗабыли await перед res.json()Два await: fetch и json
Cannot use import outside a moduleNode без ESMФайл .mjs или "type":"module"
Старые данные на экранеМедленный ответ пришёл последнимAbortController в useEffect
Двойная отправка формыДва клика по кнопкеdisabled=&#123;loading&#125; на кнопке

Шпаргалка — скопировать в тетрадь

// === fetch: GET ===
const res = await fetch(url);
if (!res.ok) throw new Error(res.status);
const data = await res.json();

// === fetch: POST JSON ===
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' }),
});

// === axios: GET / POST ===
import axios from 'axios';
const { data } = await axios.get(url, { params: { q: 1 } });
await axios.post(url, { key: 'value' });

// === axios: клиент ===
const api = axios.create({ baseURL: '/api', timeout: 10000 });

Чек-лист перед сдачей лабораторной

  • В отчёте есть код и фрагмент ответа (JSON или код статуса).
  • Для POST указан JSON и правильный метод.
  • Ошибки HTTP обработаны (res.ok или try/catch).
  • Токены не в Git.
  • При Failed to fetch — скрин curl с тем же URL.
  • Указан таймаут или пояснение, зачем он нужен.

Связанные материалы

ТемаСсылка
curl, health-check, Python requestscurl / fetch — API-запросы
Массивы, debounce, общий JSПримеры JavaScript
DOM в браузереJavaScript DOM — 30 приёмов
Promise, async/awaitАсинхронное программирование
Каркас API-клиентаПрактика JavaScript
AbortController, SSEОтмена запросов
React и данныеReact — о разделе
Формат галереиTurtle на Python
Файлы и текст PythonPython — файлы и текст

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").