Пошук уроків, статей та іншого контенту
Навчіться обирати між Route Handler і Server Action залежно від клієнта, протоколу та вимог інтеграції.
Route Handler і Server Action виконуються на сервері, але вирішують різні задачі:
Route Handler створює HTTP-ендпоінт у застосунку Next.js.
Server Action дає серверну функцію, яку може викликати React-інтерфейс через механізми Next.js.
Тому вибір залежить не лише від того, де виконується код, а й від того:
хто є клієнтом;
який протокол потрібен;
чи має інтеграція бути публічною та стабільною;
чи потрібні HTTP-методи, статуси, заголовки й контроль формату відповіді.
Використовуйте Route Handler, якщо вам потрібен HTTP API.
Route Handler підходить, коли endpoint викликають:
мобільний застосунок;
frontend, написаний не на Next.js;
інший сервер;
CLI-утиліта;
партнерська інтеграція;
платіжний або поштовий сервіс;
webhook від зовнішньої системи.
Якщо API має бути окремим контрактом для клієнтів, краще створити Route Handler.
Наприклад:
GET /api/products
POST /api/orders
DELETE /api/orders/:idЦе зручніше для:
документування API;
версіювання, наприклад /api/v1/orders;
тестування через curl або Postman;
явного контролю статусів HTTP;
підтримки JSON, multipart form data або інших форматів;
інтеграції з OAuth або API-ключами.
Webhook завжди повинен приймати HTTP-запит від зовнішнього сервісу, тому для нього потрібен Route Handler.
Обробник webhook зазвичай має:
прийняти тіло запиту;
перевірити підпис;
перевірити ідемпотентність події;
повернути відповідний HTTP-статус.
Server Action для цього не підходить як публічний контракт: зовнішній сервіс очікує звичайний URL і визначений формат HTTP-взаємодії.
У Route Handler можна явно повернути:
статус 200, 201, 204, 400, 401, 404, 409 або 500;
HTTP-заголовки;
Content-Type;
cookies;
redirect;
потокову відповідь.
Це важливо, коли поведінка endpoint є частиною контракту для іншої системи.
Файл app/api/orders/route.ts:
type CreateOrderBody = {
productId: string;
quantity: number;
};
function isCreateOrderBody(value: unknown): value is CreateOrderBody {
if (!value || typeof value !== "object") {
return false;
}
const body = value as Record<string, unknown>;
return (
typeof body.productId === "string" &&
body.productId.length > 0 &&
typeof body.quantity === "number" &&
Number.isInteger(body.quantity) &&
body.quantity > 0
);
}
export async function POST(request: Request) {
let body: unknown;
try {
body = await request.json();
} catch {
return Response.json(
{ error: "Тіло запиту має бути коректним JSON" },
{ status: 400 },
);
}
if (!isCreateOrderBody(body)) {
return Response.json(
{
error: "Потрібні productId і додатне ціле quantity",
},
{ status: 422 },
);
}
// У реальному застосунку тут викликається сервіс або репозиторій.
const order = {
id: crypto.randomUUID(),
productId: body.productId,
quantity: body.quantity,
status: "created",
};
return Response.json(order, { status: 201 });
}Такий endpoint можна викликати з будь-якого HTTP-клієнта:
curl -X POST http://localhost:3000/api/orders \
-H "Content-Type: application/json" \
-d '{"productId":"book-42","quantity":2}'У відповіді клієнт отримає звичайний HTTP-статус і JSON. Йому не потрібно знати, що endpoint реалізований у Next.js.
Використовуйте Server Action, якщо операція є серверною дією конкретного інтерфейсу Next.js.
Server Action добре підходить для:
створення запису з форми;
редагування профілю;
зміни налаштувань;
додавання товару до кошика;
видалення власного ресурсу;
виконання іншої мутації, яка належить UI цього застосунку.
Дія може бути безпосередньо передана у action форми:
<form action={createOrder}>
...
</form>У такому сценарії не потрібно вручну створювати fetch, обробляти URL endpoint і перетворювати дані форми на JSON.
Якщо єдиний клієнт — ваш застосунок на Next.js, Server Action часто має простішу модель:
серверна логіка залишається на сервері;
форма викликає функцію;
не потрібно проєктувати публічний HTTP API;
після мутації можна інвалідовувати кеш через revalidatePath або revalidateTag;
помилку можна повернути безпосередньо як результат дії.
Файл app/orders/actions.ts:
"use server";
import { revalidatePath } from "next/cache";
type CreateOrderResult =
| { ok: true; orderId: string }
| { ok: false; error: string };
export async function createOrder(
formData: FormData,
): Promise<CreateOrderResult> {
const productId = formData.get("productId");
const quantityValue = formData.get("quantity");
if (typeof productId !== "string" || productId.length === 0) {
return {
ok: false,
error: "Вкажіть товар",
};
}
const quantity = Number(quantityValue);
if (!Number.isInteger(quantity) || quantity <= 0) {
return {
ok: false,
error: "Кількість має бути додатним цілим числом",
};
}
// У реальному застосунку тут викликається сервіс або репозиторій.
const orderId = crypto.randomUUID();
revalidatePath("/orders");
return {
ok: true,
orderId,
};
}Файл app/orders/page.tsx:
import { createOrder } from "./actions";
export default function OrdersPage() {
return (
<main>
<h1>Нове замовлення</h1>
<form action={createOrder}>
<label>
Ідентифікатор товару
<input name="productId" required />
</label>
<label>
Кількість
<input
name="quantity"
type="number"
min="1"
step="1"
required
/>
</label>
<button type="submit">Створити замовлення</button>
</form>
</main>
);
}Це не означає, що Server Action є «кращим API». Він просто краще відповідає цьому конкретному сценарію: форма вашого Next.js-застосунку створює серверну мутацію.
Оберіть Server Action, якщо:
операція виконується з форми або компонента;
не потрібно відкривати окремий HTTP-контракт;
результат потрібен саме цьому UI;
після мутації потрібно оновити Next.js-кеш;
ви хочете передавати FormData або параметри дії замість ручного HTTP-коду.
Оберіть Route Handler, якщо:
клієнтський код має працювати через HTTP;
запит виконується з Client Component через fetch;
потрібно явно керувати статусами, заголовками або форматом відповіді;
endpoint може стати API для інших клієнтів.
Використовуйте Route Handler.
Такі клієнти не повинні залежати від внутрішньої структури React-компонентів або Server Actions. Для них потрібен стабільний HTTP-контракт.
Використовуйте Route Handler.
Це стосується:
webhook;
callback після оплати;
інтеграції з CRM;
імпорту даних;
автоматизованого обміну між сервісами.
Оберіть Route Handler, якщо ви мислите категоріями:
GET, POST, PUT, PATCH, DELETE;
URL;
HTTP-заголовків;
кодів статусу;
JSON або іншого формату тіла;
публічного контракту.
Оберіть Server Action, якщо операція виглядає як:
«Коли користувач відправляє цю форму, виконай серверну мутацію і поверни результат для цього інтерфейсу».
У цьому випадку HTTP є транспортним механізмом Next.js, але HTTP-контракт не є основною частиною вашого доменного дизайну.
Route Handler є кращим вибором, якщо endpoint потрібно:
використовувати незалежно від Next.js;
версіювати;
тестувати окремо від React;
описати для сторонніх розробників;
викликати з декількох типів клієнтів;
захистити API-ключем або іншим протоколом авторизації.
Server Action доречний, коли:
API не має бути загальнодоступним;
дію викликає тільки ваш UI;
не потрібен окремий REST-контракт;
важливі інтеграція з формами та оновлення UI;
зміна даних тісно пов’язана з конкретною сторінкою або компонентом.
Іноді одна й та сама операція потрібна і UI, і зовнішньому API. Не обов’язково, щоб один механізм викликав інший через HTTP.
Краще винести бізнес-операцію в окремий серверний модуль:
app/api/orders/route.ts ─┐
├─> server/order-service.ts
app/orders/actions.ts ─┘Route Handler відповідає за HTTP, а Server Action — за інтеграцію з формою. Обидва можуть викликати спільний сервіс.
// server/order-service.ts
export type CreateOrderInput = {
productId: string;
quantity: number;
};
export async function createOrder(input: CreateOrderInput) {
if (input.quantity <= 0) {
throw new Error("Кількість має бути додатною");
}
// Тут розміщується робота з базою даних або зовнішнім сервісом.
return {
id: crypto.randomUUID(),
...input,
};
}Важливо розділяти відповідальність:
Route Handler перетворює HTTP-запит на вхідні дані й результат на HTTP-відповідь.
Server Action перетворює дані форми на вхідні дані й результат на стан для UI.
сервіс містить бізнес-правила.
Не потрібно викликати власний Route Handler через fetch із Server Action лише для повторного використання логіки. Це додає зайвий HTTP-запит і змішує транспортний шар із бізнес-логікою.
Незалежно від вибору механізму:
не довіряйте даним форми або HTTP-запиту;
перевіряйте типи, формат і діапазони значень на сервері;
перевіряйте права доступу на сервері;
не покладайтеся лише на приховані поля або перевірку в UI;
розглядайте Server Action як доступну для виклику серверну функцію, а не як приватний код, який неможливо викликати ззовні.
У Route Handler відсутній користувач автоматично. У Server Action також потрібно явно отримати поточного користувача через вашу систему автентифікації та перевірити його права перед мутацією.
Не кожна форма потребує окремого /api/... endpoint.
Якщо форма належить вашому Next.js UI, а дія не потрібна іншим клієнтам, Server Action часто буде простішим рішенням.
Server Action не варто проєктувати як API для мобільного клієнта або сторонньої компанії. Для такого сценарію потрібен явний HTTP-контракт через Route Handler.
Поширена зайва схема:
Server Action -> fetch("/api/orders") -> Route Handler -> база данихЯкщо обидва шари працюють у вашому застосунку, краще викликати спільний серверний сервіс напряму:
Server Action -> сервіс -> база даних
Route Handler -> сервіс -> база данихШвидкодія не є головним критерієм вибору. Важливіше визначити:
хто викликає операцію;
чи потрібен публічний HTTP-контракт;
чи є це частиною UI або інтеграцією;
чи потрібні HTTP-методи й статуси.
Route Handler має повертати HTTP-відповідь:
return Response.json({ error: "Не знайдено" }, { status: 404 });Server Action може повернути серіалізований результат для UI:
return {
ok: false,
error: "Не знайдено",
};Не потрібно змушувати Server Action імітувати REST-відповідь, якщо ця відповідь не використовується як HTTP-контракт.
Валідація в HTML або Client Component потрібна для зручності користувача, але не є захистом. Дані необхідно повторно перевіряти в Server Action або Route Handler перед виконанням операції.
Поставте такі запитання:
Чи викликатиме операцію зовнішній клієнт?
Так — Route Handler.
Чи потрібен webhook або звичайний HTTP endpoint?
Так — Route Handler.
Чи потрібно явно контролювати методи, статуси та заголовки?
Так — Route Handler.
Чи є єдиним клієнтом форма або UI вашого Next.js-застосунку?
Так — розгляньте Server Action.
Чи є операція мутацією, пов’язаною з конкретним UI?
Так — Server Action зазвичай буде природнішим вибором.
Чи потрібні обидва варіанти?
Винесіть бізнес-логіку в спільний серверний сервіс, а транспортні адаптери реалізуйте окремо.
Route Handler — це HTTP endpoint для браузера, мобільного застосунку, іншого сервера або зовнішнього сервісу.
Server Action — це серверна дія, інтегрована з UI та формами Next.js.
Для webhook, публічного API й міжсервісної інтеграції використовуйте Route Handler.
Для внутрішніх мутацій вашого Next.js UI використовуйте Server Action.
Не викликайте власний Route Handler із Server Action без потреби.
Спільну бізнес-логіку виносьте в окремий серверний сервіс, а вибір між механізмами робіть за клієнтом, протоколом і вимогами інтеграції.