REST API — интерфейс, в котором клиент работает с ресурсами через общие правила обмена. В веб-разработке это обычно HTTP-запросы: получить задачу, создать заказ, изменить профиль. Ресурс имеет адрес, метод задаёт действие, ответ сообщает результат и при необходимости возвращает данные.
Термин REST часто используют для любого HTTP API. Строгое значение уже: система должна соблюдать архитектурные ограничения REST. MDN отдельно отмечает эту разницу.
Ресурс, URL и эндпоинт: что чем называется
Представь учебный сервис задач. У задачи есть номер 42, название Read API docs и признак завершения. /tasks/42 — адрес этой задачи, /tasks — адрес коллекции. Сам ресурс и его JSON-представление различаются: одна задача может отдаваться в нескольких форматах.
В документации API эндпоинтом часто называют сочетание метода и пути: GET /tasks/42. По тому же пути сервер может принимать DELETE, но это уже другая операция. Конкретные пути и имена полей задаёт договорённость API, а не HTTP.
Пример представления задачи:
{
"id": 42,
"title": "Read API docs",
"done": false
}
URL https://api.example.test/tasks?done=false&limit=20 в этом примере означает выборку незавершённых задач с ограничением размера ответа. Здесь домен условный; такого сервиса для выполнения команд статьи нет. done и limit — придуманные нами параметры, их названия и поведение нужно искать в документации конкретного API.
Из чего состоит HTTP-запрос и ответ
Вот схема обмена для чтения задачи. Показаны значимые для объяснения поля HTTP/1.1; служебные заголовки опущены.
GET /tasks/42 HTTP/1.1
Host: api.example.test
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{"id":42,"title":"Read API docs","done":false}
Accept сообщает, какие форматы ответа готов принять клиент. Content-Type описывает формат фактически отправленного тела. Эти заголовки не превращают текст в JSON: корректно сериализовать и прочитать данные всё равно должны программы.
Адрес выбирает задачу, ответ возвращает её состояние
Для создания задачи клиент отправляет тело запроса. В этом API сервер назначает номер и возвращает адрес созданного ресурса:
POST /tasks HTTP/1.1
Host: api.example.test
Content-Type: application/json
Accept: application/json
{"title":"Read API docs"}
HTTP/1.1 201 Created
Location: /tasks/42
Content-Type: application/json
{"id":42,"title":"Read API docs","done":false}
Синтаксис JSON разобран отдельно. Он удобен для таких примеров, но REST не требует именно JSON.
GET, POST, PUT, PATCH и DELETE: какой метод для чего
В этом API операции распределены так:
| Метод и путь | Действие с задачами |
|---|---|
GET /tasks/42 | Получить представление задачи |
POST /tasks | Создать задачу с номером, назначенным сервером |
PUT /tasks/42 | Задать новое состояние задачи по известному адресу |
PATCH /tasks/42 | Применить описанные в теле изменения |
DELETE /tasks/42 | Удалить ресурс по этому адресу |
Это применение семантики HTTP-методов к нашей модели. POST также используют для обработки данных и других операций; он не ограничен созданием записи. После DELETE данные могут остаться в резервных копиях сервера.
PUT задаёт замену состояния ресурса. Если API принимает частичное обновление, это должно быть явно описано, иначе пропущенные поля можно потерять. PATCH передаёт документ изменений, формат которого нужно согласовать с сервером: RFC 5789 не назначает ему единственный формат тела.
Например, при поддержке JSON Merge Patch можно отметить задачу выполненной:
PATCH /tasks/42 HTTP/1.1
Host: api.example.test
Content-Type: application/merge-patch+json
{"done":true}
В JSON Merge Patch отсутствующие поля сохраняются, а null у поля означает его удаление. Поэтому {"title":null} не равно просьбе сохранить JSON-значение null. Перед отправкой PATCH проверь формат и правила конкретного API.
Можно ли повторить запрос после тайм-аута
Тайм-аут означает, что клиент не дождался результата. Сервер мог успеть создать задачу, но ответ потерялся. Безусловный повтор POST /tasks тогда способен создать вторую задачу.
HTTP называет метод идемпотентным, если несколько одинаковых запросов имеют тот же предполагаемый эффект на сервере, что один. GET, PUT и DELETE идемпотентны по семантике метода; POST и PATCH этого в общем случае не гарантируют. Одинакового текста или статуса ответа не требуется: повторный DELETE может вернуть 404, хотя ресурс уже удалён. Определение и примеры MDN.
Для создания задачи нужна предусмотренная API защита от повторов: например, идентификатор операции с серверной дедупликацией. Самодельный заголовок с произвольным именем не поможет, если сервер не знает о нём. Правило повторов нужно читать в контракте до настройки автоматических попыток.
GET предназначен для чтения: не проектируй GET /tasks/42/delete. Такой адрес может открыть предпросмотр ссылки или другой клиент, который рассчитывает на безопасную семантику чтения. Безопасность метода здесь означает отсутствие запрошенного изменения, а не шифрование или отсутствие журналирования. RFC 9110, свойства методов.
Что REST добавляет к обычному HTTP API
В описании Филдинга REST объединяет разделение клиента и сервера, отсутствие зависимости от серверной сессии, правила кеширования, единый интерфейс и слои посредников. Передача исполняемого кода клиенту — необязательное ограничение.
Единый интерфейс включает идентификацию ресурсов, работу через представления, самодостаточные сообщения и переходы по ссылкам из ответов. Последнее называют HATEOAS. Наличие URL и JSON само по себе не доказывает соблюдение всех ограничений. Учебные примеры выше показывают HTTP-обмен без переходов по ссылкам из ответов.
Stateless не запрещает базу данных. Задачи и пользователи остаются на сервере. Ограничение касается контекста диалога: сервер должен понимать очередной запрос без скрытой зависимости от предыдущих шагов клиента. Например, запрос к конкретной задаче содержит её адрес и необходимые данные доступа, вместо расчёта на «задачу, которую этот клиент открыл раньше».
Практика: посмотреть HTTP-ответ через curl
Для чтения ответа не нужен готовый backend. Создадим один JSON-файл и отдадим его через встроенный сервер Python. Это учебная проверка GET, заголовков и ошибки 404; создание и изменение задач этот сервер не реализует.
Команды проверены в Bash и zsh на Linux с CPython 3.14.7 и curl 8.15.0. Нужны два терминала и свободный порт 18765. В первом терминале:
rest_demo_dir="$(mktemp -d)"
printf '%s\n' '{"id":42,"title":"Read API docs","done":false}' > "$rest_demo_dir/task.json"
python3 -m http.server 18765 --bind 127.0.0.1 --directory "$rest_demo_dir"
Сервер слушает только твой компьютер и отдаёт файлы отдельной временной папки. Документация Python описывает его как простой сервер, не предназначенный для production. Оставь первый терминал работающим и во втором выполни:
curl --silent --show-error --include --max-time 5 \
-H 'Accept: application/json' \
http://127.0.0.1:18765/task.json
В проверенном ответе — статус HTTP/1.0 200 OK, заголовок Content-type: application/json и созданный JSON. HTTP/1.0 здесь — настройка простого сервера Python; REST не привязан к HTTP/1.1. Дата и порядок заголовков могут отличаться.
Получим только тело и проверим значимые поля. --fail заставляет curl сообщать об HTTP-ошибках; --max-time ограничивает длительность команды. Эти параметры описаны в руководстве curl.
set -o pipefail
curl --fail --silent --show-error --max-time 5 \
http://127.0.0.1:18765/task.json \
| python3 -c 'import json,sys; task=json.load(sys.stdin); assert task["id"] == 42 and task["done"] is False; print("Задача 42 прочитана")'
Ожидаемый вывод: Задача 42 прочитана. Теперь запроси отсутствующий ресурс:
curl --silent --show-error --max-time 5 --output /dev/null \
--write-out '%{http_code}\n' http://127.0.0.1:18765/missing.json
Результат — 404. В этом случае простой файловый сервер возвращает HTML с ошибкой, хотя путь заканчивается на .json. Поэтому клиенту нужно проверять статус и фактический тип ответа до разбора JSON. Разницу кодов ошибок смотри в справочнике HTTP-статусов.
Завершив упражнение, нажми Ctrl+C в первом терминале. В нём же удали только созданный учебный файл и теперь пустую папку:
rm -- "$rest_demo_dir/task.json"
rmdir -- "$rest_demo_dir"
Что проверить перед подключением к чужому API
Возьми документацию одного эндпоинта и найди метод, полный адрес, правила доступа, обязательные поля и примеры ошибок. Затем уточни пагинацию, лимит запросов и поведение при повторе операции. Если этих сведений нет, одного примера успешного JSON недостаточно для надёжного клиента.
Сначала добейся правильного ответа в curl и проверь его поля, как в упражнении. Следующий шаг — выполнить такой же запрос из кода: Requests в Python разбирает параметры, JSON, статусы и тайм-ауты на локальном учебном API.