Урок 23 из 49 · Месяц 3. JavaScript, часть 2

REST API, HTTP и HTTPS, методы PUT и DELETE

Содержание урока

Мы уже умеем получать данные методом GET и создавать их методом POST. Пора разобраться, по каким правилам вообще устроены API, с которыми работает фронтенд. В этом уроке поговорим о протоколах HTTP и HTTPS, разберём подход REST API, научимся изменять и удалять данные методами PUT и DELETE, а в конце установим Node.js — он понадобится уже в следующем уроке и дальше на протяжении всего курса.

HTTP — язык общения браузера и сервера

HTTP (HyperText Transfer Protocol) — это набор правил, по которым клиент и сервер обмениваются сообщениями. Как правила оформления письма: адрес получателя, тема, текст. Если обе стороны соблюдают правила, они понимают друг друга.

Каждое общение — это пара запрос → ответ. Вот как выглядит HTTP-запрос «изнутри» (так его видит сервер):

PUT /posts/1 HTTP/1.1
Host: jsonplaceholder.typicode.com
Content-Type: application/json

{"id":1,"title":"Новый заголовок","body":"Новый текст","userId":1}
  • Первая строка: метод (PUT), путь (/posts/1) и версия протокола.
  • Дальше заголовки — служебная информация в виде Имя: значение.
  • После пустой строки — тело запроса.

Ответ сервера устроен похоже:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":1,"title":"Новый заголовок","body":"Новый текст","userId":1}

Первая строка ответа содержит статус-код. Напомним группы:

Группа Смысл Примеры
1xx информационные редко встречаются во фронтенде
2xx успех 200 OK, 201 Created, 204 No Content
3xx перенаправление 301 Moved Permanently, 304 Not Modified
4xx ошибка клиента 400, 401, 403 Forbidden, 404, 422
5xx ошибка сервера 500, 502 Bad Gateway, 503

Важные заголовки

Заголовок Для чего
Content-Type в каком формате тело: application/json, text/html
Accept какой формат ответа клиент хочет получить
Authorization токен доступа, например Bearer YOUR_TOKEN

HTTPS — защищённый HTTP

Обычный HTTP передаёт данные открытым текстом. Любой, кто находится «между» вами и сервером (например, владелец публичного Wi-Fi), может прочитать пароль или подменить страницу.

HTTPS — это тот же HTTP, но внутри зашифрованного канала (протокол TLS). Аналогия: HTTP — открытка, которую по пути может прочитать любой почтальон; HTTPS — письмо в запечатанном конверте, который может открыть только получатель.

Что даёт HTTPS:

  • шифрование — посторонние не прочитают данные;
  • целостность — данные нельзя незаметно изменить по дороге;
  • подлинность — сертификат подтверждает, что вы говорите именно с нужным сайтом.

Что такое REST API

API (Application Programming Interface) — это «меню» сервера: список адресов и действий, которые он умеет выполнять. REST — популярный стиль, по которому это меню удобно организовать.

Главная идея REST: всё на сервере — это ресурсы (пользователи, посты, товары), у каждого ресурса есть свой адрес (URL), а действие определяется HTTP-методом.

Аналогия — библиотека. Книги — ресурсы, у каждой есть номер на полке (URL). Вы можете взять книгу почитать (GET), сдать новую (POST), заменить испорченную (PUT), исправить опечатку (PATCH) или списать (DELETE).

Ресурсы и адреса

Адрес Что это
/posts коллекция — все посты
/posts/1 один конкретный пост с id = 1
/posts/1/comments комментарии к посту 1
/users/3/todos задачи пользователя 3

В адресе принято писать существительные во множественном числе, а не глаголы. Не /getPosts или /deletePost?id=1, а /posts и /posts/1 с нужным методом.

Методы — что делаем с ресурсом

Метод Действие Пример Тело запроса Типичный ответ
GET получить GET /posts/1 нет 200 + данные
POST создать POST /posts новый объект 201 + созданный объект
PUT заменить целиком PUT /posts/1 весь объект 200 + обновлённый объект
PATCH изменить частично PATCH /posts/1 только изменённые поля 200 + обновлённый объект
DELETE удалить DELETE /posts/1 нет 200 или 204

Эти четыре базовые операции часто называют CRUD: Create (создать), Read (прочитать), Update (обновить), Delete (удалить).

Другие принципы REST

  • Без сохранения состояния (stateless). Сервер не помнит предыдущих запросов клиента. Каждый запрос должен содержать всё нужное, например токен авторизации.
  • Единый формат. Обычно JSON и в запросах, и в ответах.
  • Предсказуемость. Зная адрес /posts, легко догадаться про /posts/5 и /users.

Метод PUT — заменить запись

PUT отправляет полную новую версию ресурса. Всё, что вы не передали, может быть потеряно.

const API_URL = "https://jsonplaceholder.typicode.com";

async function updatePost(id, post) {
  const response = await fetch(`${API_URL}/posts/${id}`, {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(post),
  });

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

updatePost(1, {
  id: 1,
  title: "Обновлённый заголовок",
  body: "Полностью новый текст поста",
  userId: 1,
}).then((post) => console.log(post));

Разбор:

  • Адрес указывает на конкретную запись — /posts/1, а не на всю коллекцию.
  • В теле — все поля объекта, даже те, что не менялись (userId).
  • Сервер возвращает обновлённую запись.

PATCH — изменить только часть

Если нужно поменять одно поле, удобнее PATCH: передаём только то, что меняется.

async function patchPost(id, changes) {
  const response = await fetch(`${API_URL}/posts/${id}`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(changes),
  });

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

patchPost(1, { title: "Только новый заголовок" }).then((post) => {
  console.log(post.title); // "Только новый заголовок"
  console.log(post.body);  // старый текст сохранился
});

Метод DELETE — удалить запись

У DELETE обычно нет тела: всё нужное есть в адресе.

async function deletePost(id) {
  const response = await fetch(`${API_URL}/posts/${id}`, {
    method: "DELETE",
  });

  if (!response.ok) throw new Error(`Не удалось удалить: ${response.status}`);
  return true;
}

deletePost(1).then(() => console.log("Пост удалён"));

Сервер может ответить 200 с телом или 204 No Content без тела. Поэтому после DELETE не вызывайте response.json() бездумно: у пустого ответа это вызовет ошибку.

Практика: список задач с полным CRUD

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

<ul id="todos"></ul>
<script src="todos.js"></script>
// todos.js
const API_URL = "https://jsonplaceholder.typicode.com";
const list = document.querySelector("#todos");

async function request(path, options = {}) {
  const response = await fetch(`${API_URL}${path}`, {
    headers: { "Content-Type": "application/json" },
    ...options,
  });
  if (!response.ok) throw new Error(`Ошибка ${response.status}`);
  return response.status === 204 ? null : response.json();
}

function renderTodo(todo) {
  const li = document.createElement("li");
  li.dataset.id = todo.id;

  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";
  checkbox.checked = todo.completed;

  const title = document.createElement("span");
  title.textContent = todo.title;

  const removeBtn = document.createElement("button");
  removeBtn.type = "button";
  removeBtn.textContent = "Удалить";

  checkbox.addEventListener("change", async () => {
    try {
      await request(`/todos/${todo.id}`, {
        method: "PATCH",
        body: JSON.stringify({ completed: checkbox.checked }),
      });
    } catch (error) {
      checkbox.checked = !checkbox.checked; // откатываем галочку
      alert("Не удалось сохранить");
    }
  });

  removeBtn.addEventListener("click", async () => {
    if (!confirm(`Удалить «${todo.title}»?`)) return;
    try {
      await request(`/todos/${todo.id}`, { method: "DELETE" });
      li.remove();
    } catch (error) {
      alert("Не удалось удалить");
    }
  });

  li.append(checkbox, title, removeBtn);
  return li;
}

async function init() {
  try {
    const todos = await request("/todos?_limit=5");
    list.append(...todos.map(renderTodo));
  } catch (error) {
    list.textContent = "Не удалось загрузить задачи";
  }
}

init();

Что здесь нового:

  • Функция request — обёртка над fetch. Она подставляет базовый адрес и заголовок, проверяет ok и разбирает JSON. Каждый новый запрос — одна строка. Такие обёртки есть почти в каждом проекте.
  • ...options — spread: настройки, переданные при вызове (method, body), добавляются к стандартным.
  • Откат галочки. Если PATCH не удался, возвращаем чекбокс в прежнее состояние, чтобы интерфейс не врал пользователю.
  • list.append(...todos.map(renderTodo)) — превращаем каждую задачу в <li> и добавляем все сразу.

Практика от менторов и установка Node.js

На практическом занятии ментор разберёт с группой свой пример API и покажет, как читать его документацию. Чтобы быть готовыми, установите Node.js заранее.

Что такое Node.js

Node.js — программа, которая запускает JavaScript вне браузера: прямо на компьютере, в терминале. Вместе с ним устанавливается npm — менеджер пакетов, с которым мы познакомимся в уроке про npm и package.json. Без Node.js не работают ни Vite, ни React-проекты, ни большинство инструментов фронтендера.

Установка

  1. Откройте официальный сайт nodejs.org и скачайте версию LTS (Long Term Support — версия с долгой поддержкой, самая стабильная). Чётные версии (например, 22 или 24) — это LTS-линейки.
  2. Запустите установщик и нажимайте «Далее», оставляя настройки по умолчанию.
  3. Перезапустите терминал (или VS Code), чтобы он «увидел» новую программу.
  4. Проверьте установку:
node -v
npm -v

Если в ответ появились номера версий, например v24.x.x и 11.x.x, — всё готово. Точные цифры у вас могут отличаться, главное — что команды работают.

Первый скрипт на Node.js

Создайте файл hello.js:

const name = "Атлас";
console.log(`Привет из Node.js, ${name}!`);
console.log("Версия Node:", process.version);

Запустите его в терминале из той же папки:

node hello.js

Вы увидите вывод прямо в терминале. В Node.js нет document и window — нет страницы, — зато есть доступ к файлам и сети. А ещё в нём тоже есть fetch:

// users.js
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const users = await response.json();
console.log(users.map((u) => u.name));

Чтобы await работал на верхнем уровне, сохраните файл с расширением .mjs (users.mjs) — так Node.js поймёт, что это модуль — и запустите node users.mjs.

Если что-то пошло не так

  • node: command not found или «не является внутренней командой» — терминал не перезапущен, либо при установке не была отмечена опция добавления в PATH. Перезапустите компьютер или переустановите Node.js.
  • В PowerShell ошибка про «выполнение сценариев отключено» при запуске npm — используйте Git Bash или Командную строку, либо спросите ментора, как поменять политику выполнения.

Практика

  1. Разобрать адреса. Для интернет-магазина запишите REST-адреса и методы для действий: получить все товары, получить товар 7, создать товар, изменить цену товара 7, удалить товар 7, получить отзывы к товару 7.
  2. PUT. Через fetch отправьте PUT на /posts/1 с новым заголовком и текстом. Выведите ответ в консоль и убедитесь, что статус — 200.
  3. PATCH и DELETE. Отправьте PATCH на /todos/1 с { completed: true }, затем DELETE на /todos/1. Для каждого запроса выведите в консоль response.status.
  4. Редактирование поста. Загрузите пост /posts/1 и подставьте его в форму (input для заголовка, textarea для текста). При отправке формы — PUT-запрос и сообщение «Сохранено».
  5. Node.js. Установите Node.js, проверьте node -v и npm -v, затем напишите скрипт todos.mjs, который загружает задачи пользователя 1 и выводит в терминал количество выполненных и невыполненных.

Итоги

  • HTTP — правила обмена «запрос → ответ»: метод, адрес, заголовки, тело и статус-код.
  • HTTPS — тот же HTTP, но зашифрованный; для форм с личными данными — только он.
  • REST: ресурсы с понятными адресами (/posts/1), действие задаёт метод.
  • CRUD: POST — создать, GET — прочитать, PUT/PATCH — обновить, DELETE — удалить.
  • PUT заменяет запись целиком, PATCH меняет только переданные поля.
  • Node.js запускает JavaScript вне браузера; ставим версию LTS и проверяем node -v.
Отзыв