Пошук уроків, статей та іншого контенту
Розберете архітектурні принципи REST, HTTP-методи, статус-коди та формати обміну даними між клієнтом і сервером.
REST (Representational State Transfer) — це архітектурний стиль для створення вебсервісів. REST описує правила взаємодії клієнта із сервером через HTTP.
У REST:
клієнт надсилає запит;
сервер обробляє запит;
сервер повертає відповідь;
дані передаються у певному форматі, найчастіше JSON.
Клієнтом може бути браузер, мобільний застосунок або інша серверна програма. Сервер відповідає за дані та бізнес-логіку.
REST — це не окрема мова програмування і не бібліотека Node.js. Це набір архітектурних принципів.
У REST дані подаються як ресурси. Ресурсом може бути:
користувач;
товар;
стаття;
замовлення;
повідомлення.
Кожен ресурс має адресу — URL.
Наприклад, API для роботи з книгами може мати такі адреси:
GET /books
GET /books/10
POST /books
PUT /books/10
DELETE /books/10Тут:
/books — колекція книг;
/books/10 — книга з ідентифікатором 10;
HTTP-метод визначає операцію над ресурсом.
URL зазвичай описує що є ресурсом, а HTTP-метод — що з ним потрібно зробити.
Клієнт і сервер виконують різні ролі:
клієнт відповідає за інтерфейс і надсилання запитів;
сервер відповідає за зберігання даних і їх обробку.
Вони можуть розроблятися та змінюватися незалежно один від одного, якщо формат взаємодії залишається сумісним.
REST-запит має бути самодостатнім. Сервер не повинен покладатися на те, що він пам’ятає попередній запит клієнта.
Наприклад, якщо серверу потрібна інформація про користувача, вона має бути передана в поточному запиті — наприклад, у заголовку авторизації.
Цей принцип називають stateless — без стану.
Для однакових типів операцій використовуються однакові правила:
GET отримує дані;
POST створює дані;
PUT оновлює дані;
DELETE видаляє дані.
Завдяки цьому клієнту легше працювати з різними REST API.
Сервер повертає не сам ресурс як внутрішній об’єкт, а його представлення. Найпоширеніший формат такого представлення — JSON.
Наприклад, внутрішній запис про користувача може бути представлений так:
{
"id": 1,
"name": "Олена",
"email": "olena@example.com"
}Метод GET використовується для отримання даних.
Отримати список користувачів:
GET /usersОтримати одного користувача:
GET /users/1GET не повинен змінювати дані на сервері.
Метод POST використовується для створення нового ресурсу.
POST /usersДані нового користувача зазвичай передаються в тілі запиту:
{
"name": "Олена",
"email": "olena@example.com"
}Якщо ресурс успішно створено, сервер часто повертає статус 201 Created.
Метод PUT використовується для повної заміни або оновлення ресурсу.
PUT /users/1Приклад тіла запиту:
{
"name": "Олена Коваль",
"email": "olena.koval@example.com"
}Під час використання PUT клієнт зазвичай передає повне представлення ресурсу.
Метод PATCH використовується для часткового оновлення ресурсу.
PATCH /users/1Наприклад, можна змінити лише ім’я:
{
"name": "Олена Коваль"
}Метод DELETE використовується для видалення ресурсу.
DELETE /users/1HTTP-запит може містити:
метод;
URL;
заголовки;
тіло запиту.
Приклад запиту на створення користувача:
POST /users HTTP/1.1
Host: example.com
Content-Type: application/json
{
"name": "Олена",
"email": "olena@example.com"
}Заголовок Content-Type: application/json повідомляє серверу, що тіло запиту містить JSON.
HTTP-відповідь містить:
статус-код;
заголовки;
тіло відповіді.
Приклад:
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": 1,
"name": "Олена",
"email": "olena@example.com"
}Заголовок Content-Type у відповіді повідомляє клієнту, у якому форматі надіслані дані.
Статус-код показує результат обробки запиту.
200 OK — запит успішно виконано;
201 Created — ресурс успішно створено;
204 No Content — операція успішна, але тіло відповіді відсутнє.
400 Bad Request — запит має неправильний формат або некоректні дані;
401 Unauthorized — потрібна автентифікація;
403 Forbidden — доступ заборонено;
404 Not Found — ресурс не знайдено;
409 Conflict — конфлікт із поточним станом ресурсу.
500 Internal Server Error — внутрішня помилка сервера;
503 Service Unavailable — сервер тимчасово недоступний.
Клієнт може використовувати статус-код, щоб зрозуміти, чи успішним був запит і як обробити результат.
REST API може використовувати різні формати даних, але найчастіше використовується JSON.
JSON підтримує:
рядки;
числа;
логічні значення;
масиви;
об’єкти;
null.
Приклад відповіді зі списком ресурсів:
[
{
"id": 1,
"title": "Вивчити Node.js"
},
{
"id": 2,
"title": "Створити REST API"
}
]Назви властивостей JSON мають бути взяті в подвійні лапки.
Нижче наведено невеликий сервер без додаткових бібліотек. Він підтримує операції над списком завдань:
отримання всіх завдань;
отримання одного завдання;
створення завдання;
повне оновлення завдання;
видалення завдання.
Збережіть код у файлі server.js і запустіть командою node server.js.
const http = require('node:http');
const { URL } = require('node:url');
let tasks = [
{ id: 1, title: 'Вивчити REST', completed: false },
{ id: 2, title: 'Створити API', completed: false }
];
let nextId = 3;
function sendJson(response, statusCode, data) {
response.writeHead(statusCode, {
'Content-Type': 'application/json; charset=utf-8'
});
response.end(data === undefined ? '' : JSON.stringify(data));
}
function readJsonBody(request) {
return new Promise((resolve, reject) => {
let body = '';
request.on('data', (chunk) => {
body += chunk;
});
request.on('end', () => {
if (body.trim() === '') {
resolve({});
return;
}
try {
resolve(JSON.parse(body));
} catch {
reject(new Error('Некоректний JSON'));
}
});
request.on('error', reject);
});
}
const server = http.createServer(async (request, response) => {
const requestUrl = new URL(
request.url,
`http://${request.headers.host}`
);
const pathParts = requestUrl.pathname.split('/').filter(Boolean);
const isTasksRoute = pathParts[0] === 'tasks';
const taskId = pathParts.length === 2 ? Number(pathParts[1]) : null;
if (!isTasksRoute || (pathParts.length === 2 && !Number.isInteger(taskId))) {
sendJson(response, 404, { error: 'Маршрут не знайдено' });
return;
}
if (request.method === 'GET' && pathParts.length === 1) {
sendJson(response, 200, tasks);
return;
}
if (request.method === 'GET' && pathParts.length === 2) {
const task = tasks.find((item) => item.id === taskId);
if (!task) {
sendJson(response, 404, { error: 'Завдання не знайдено' });
return;
}
sendJson(response, 200, task);
return;
}
if (request.method === 'POST' && pathParts.length === 1) {
try {
const data = await readJsonBody(request);
if (typeof data.title !== 'string' || data.title.trim() === '') {
sendJson(response, 400, {
error: 'Поле title має бути непорожнім рядком'
});
return;
}
const task = {
id: nextId++,
title: data.title.trim(),
completed: Boolean(data.completed)
};
tasks.push(task);
sendJson(response, 201, task);
} catch (error) {
sendJson(response, 400, { error: error.message });
}
return;
}
if (request.method === 'PUT' && pathParts.length === 2) {
const taskIndex = tasks.findIndex((item) => item.id === taskId);
if (taskIndex === -1) {
sendJson(response, 404, { error: 'Завдання не знайдено' });
return;
}
try {
const data = await readJsonBody(request);
if (typeof data.title !== 'string' || data.title.trim() === '') {
sendJson(response, 400, {
error: 'Поле title має бути непорожнім рядком'
});
return;
}
tasks[taskIndex] = {
id: taskId,
title: data.title.trim(),
completed: Boolean(data.completed)
};
sendJson(response, 200, tasks[taskIndex]);
} catch (error) {
sendJson(response, 400, { error: error.message });
}
return;
}
if (request.method === 'DELETE' && pathParts.length === 2) {
const taskIndex = tasks.findIndex((item) => item.id === taskId);
if (taskIndex === -1) {
sendJson(response, 404, { error: 'Завдання не знайдено' });
return;
}
tasks.splice(taskIndex, 1);
sendJson(response, 204);
return;
}
sendJson(response, 405, { error: 'Метод не підтримується' });
});
server.listen(3000, () => {
console.log('REST API запущено на http://localhost:3000');
});Отримати список завдань:
curl http://localhost:3000/tasksОтримати завдання з ідентифікатором 1:
curl http://localhost:3000/tasks/1Створити завдання:
curl -X POST http://localhost:3000/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Перевірити API","completed":false}'Оновити завдання:
curl -X PUT http://localhost:3000/tasks/1 \
-H "Content-Type: application/json" \
-d '{"title":"Вивчити HTTP-методи","completed":true}'Видалити завдання:
curl -X DELETE http://localhost:3000/tasks/1У цьому прикладі масив tasks використовується замість бази даних. Після перезапуску сервера створені або видалені записи повернуться до початкового стану.
Не варто використовувати GET для видалення або зміни даних:
GET /delete-task?id=1Краще використовувати метод, який відповідає операції:
DELETE /tasks/1URL має описувати ресурс, а не дію.
Невдалий варіант:
POST /createTaskКращий варіант:
POST /tasksСаме метод POST уже вказує, що потрібно створити ресурс.
Не слід завжди повертати 200 OK, навіть якщо ресурс не знайдено. Для відсутнього ресурсу потрібно використовувати:
404 Not FoundСтатус-код є важливою частиною контракту API.
Content-TypeЯкщо тіло запиту містить JSON, потрібно вказати:
Content-Type: application/jsonСервер також повинен вказувати цей формат у відповіді.
Дані від клієнта не можна вважати правильними автоматично. Сервер має перевіряти обов’язкові поля та їхні типи, а для некоректних даних повертати 400 Bad Request.
REST — це архітектурний стиль для взаємодії клієнта і сервера через HTTP.
Дані в REST подаються як ресурси, доступні за URL.
HTTP-метод описує операцію над ресурсом.
GET отримує дані, POST створює, PUT повністю оновлює, PATCH частково оновлює, DELETE видаляє.
Статус-коди повідомляють про результат обробки запиту.
JSON є найпоширенішим форматом обміну даними в REST API.
REST-запити мають бути самодостатніми, а клієнт і сервер — розділеними.