CRUD через HTTP
CRUD через HTTP
Мы знаем REST-принципы и JSON-формат. Теперь соберём их вместе и разберём, как HTTP-методы отображаются на четыре базовые операции с данными: Create, Read, Update, Delete (CRUD). Это самый частый паттерн в веб-разработке.
Что такое CRUD
CRUD — аббревиатура четырёх операций, применимых к любому хранилищу данных:
| Операция | HTTP-метод | URL | Что делает | Статус успеха |
|---|---|---|---|---|
| Create | POST | /users | Создать новый ресурс | 201 Created |
| Read (list) | GET | /users | Получить список | 200 OK |
| Read (single) | GET | /users/42 | Получить один ресурс | 200 OK |
| Update | PUT/PATCH | /users/42 | Обновить ресурс | 200 OK |
| Delete | DELETE | /users/42 | Удалить ресурс | 204 No Content |
Важно: HTTP-метод определяет действие, URL идентифицирует ресурс. Не нужно писать /users/create или /users/delete/42 — метод уже говорит, что делать.
Create: POST /users
Клиент отправляет POST-запрос с телом, содержащим данные нового ресурса:
POST /users HTTP/1.1
Content-Type: application/json
{
"name": "Иван",
"email": "ivan@example.com"
}
Сервер создаёт ресурс и возвращает:
HTTP/1.1 201 Created
Location: /users/142
Content-Type: application/json
{
"id": 142,
"name": "Иван",
"email": "ivan@example.com",
"createdAt": "2026-04-30T12:00:00Z"
}
Ключевые моменты:
- Статус 201 Created (не 200!), чтобы клиент понимал — ресурс создан.
- Заголовок Location содержит URL нового ресурса.
- Сервер добавляет поля, которых не было в запросе (id, дата создания).
POST не обязан возвращать тело, но на практике возвращает созданный объект — клиенту нужен id.
Read (list): GET /users
GET для коллекции возвращает массив ресурсов:
GET /users HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
[
{"id": 1, "name": "Иван"},
{"id": 2, "name": "Мария"},
{"id": 3, "name": "Алексей"}
]
Для больших коллекций добавляют пагинацию через query-параметры:
GET /users?page=2&limit=20
HTTP/1.1 200 OK
{
"data": [
{"id": 21, "name": "..."},
...
],
"total": 150,
"page": 2,
"limit": 20
}
Read (single): GET /users/42
GET для конкретного элемента возвращает один объект:
GET /users/42 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 42,
"name": "Иван",
"email": "ivan@example.com",
"orders": "/users/42/orders"
}
Если ресурс не существует — 404 Not Found:
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "User not found"}
Update: PUT vs PATCH
Для обновления есть два метода с разной семантикой:
- PUT — заменить ресурс ЦЕЛИКОМ. Если пропустить поле, оно удалится (или сбросится на значение по умолчанию).
- PATCH — частичное обновление. Отправляются только поля, которые нужно изменить.
PUT /users/42
Content-Type: application/json
{"name": "Иван", "email": "new@example.com"} ← Все поля обязательны
PATCH /users/42
Content-Type: application/json
{"email": "new@example.com"} ← Только что меняется
На практике PATCH используют чаще — он безопаснее (нельзя случайно стереть поля). PUT строже и требует от клиента полного представления ресурса.
Delete: DELETE /users/42
Удаление самое простое — метод DELETE, без тела:
DELETE /users/42 HTTP/1.1
HTTP/1.1 204 No Content
204 No Content — стандартный статус для успешного DELETE (тела ответа нет). Но некоторые API возвращают 200 с удалённым объектом в теле.
Повторный DELETE на тот же URL должен возвращать 404 Not Found (ресурс уже удалён) или 204 No Content (идемпотентность: результат тот же, ресурса нет). На практике чаще 404.
Идемпотентность методов
Идемпотентность — важное свойство HTTP-методов. Метод идемпотентен, если повторный запрос даёт тот же результат, что и первый:
| Метод | Идемпотентен? | Повторный вызов |
|---|---|---|
| GET | Да | Вернёт те же данные |
| PUT | Да | Заменит теми же данными = тот же результат |
| DELETE | Да | Ресурс уже удалён = тот же результат (404 или 204) |
| POST | Нет | Создаст ещё один ресурс! |
Это важно для надёжности: если клиент не получил ответ (сеть моргнула), он может безопасно повторить GET/PUT/DELETE. POST повторять опасно — можно создать дубликат.
CRUD в реальном API: пример
Полный цикл управления пользователем:
// Создать
const createResp = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Иван', email: 'ivan@example.com' })
});
const newUser = await createResp.json(); // { id: 142, name: 'Иван', ... }
// Прочитать
const user = await fetch(`/api/users/${newUser.id}`).then(r => r.json());
// Обновить
await fetch(`/api/users/${newUser.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Иван Петров' })
});
// Удалить
await fetch(`/api/users/${newUser.id}`, { method: 'DELETE' });
Проверь себя
- Как отличить запрос на создание от запроса на чтение списка, если URL один и тот же (
/users)? - Почему POST неидемпотентен, а PUT идемпотентен?
- Какой статус-код обычно возвращается при успешном DELETE?
- По HTTP-методу: POST — создать, GET — прочитать список. Один URL, разные методы — разные действия.
- POST каждый раз создаёт новый ресурс (два POST → два пользователя). PUT заменяет ресурс целиком — два одинаковых PUT дают одинаковый результат.
- 204 No Content — успешное удаление, тела ответа нет. Некоторые API возвращают 200 с удалённым объектом.
Что унести с урока
- CRUD: Create (POST), Read (GET), Update (PUT/PATCH), Delete (DELETE) — четыре базовые операции.
- HTTP-метод определяет действие, URL определяет ресурс. Метод + URL = операция.
- PUT — полная замена, PATCH — частичное обновление.
- GET/PUT/DELETE идемпотентны — можно безопасно повторять. POST — нет.
В завершающем уроке модуля разберём, как читать документацию API — где найти эндпоинты, форматы запросов и ответов, и как тестировать API не написав ни строчки кода.