Дізнайтеся, як створювати потужні кінцеві точки API за допомогою обробників маршрутів Next.js. Цей посібник охоплює все: від базового налаштування до передових технік, з практичними прикладами та найкращими практиками.
Обробники маршрутів (Route Handlers) у Next.js: Повний посібник зі створення кінцевих точок API
Next.js революціонізував спосіб створення веб-додатків завдяки своїм потужним функціям, таким як рендеринг на стороні сервера, генерація статичних сайтів, а тепер — обробники маршрутів (Route Handlers). Обробники маршрутів надають гнучкий та ефективний спосіб створення кінцевих точок API безпосередньо у вашому додатку Next.js. Цей посібник розглядає концепцію обробників маршрутів, їхні переваги та способи їх ефективного використання для створення надійних API.
Що таке обробники маршрутів (Route Handlers) у Next.js?
Обробники маршрутів — це функції, визначені в каталозі app
проекту Next.js, які обробляють вхідні HTTP-запити. На відміну від старого підходу з pages/api
(який використовує API Routes), обробники маршрутів пропонують більш оптимізований і гнучкий спосіб визначення кінцевих точок API поряд з вашими компонентами React. По суті, це безсерверні функції, що виконуються на межі (edge) або у вибраному вами серверному середовищі.
Вважайте обробники маршрутів бекенд-логікою вашого додатку Next.js, відповідальною за обробку запитів, взаємодію з базами даних та повернення відповідей.
Переваги використання обробників маршрутів
- Колокація: Обробники маршрутів розміщуються безпосередньо поруч із вашими компонентами React у каталозі
app
, що сприяє кращій організації та підтримці коду. - Підтримка TypeScript: Вбудована підтримка TypeScript забезпечує безпеку типів та покращує досвід розробника.
- Інтеграція з проміжним ПЗ (Middleware): Легка інтеграція проміжного ПЗ для таких завдань, як автентифікація, авторизація та валідація запитів.
- Підтримка потокової передачі (Streaming): Обробники маршрутів можуть передавати дані потоково, що дозволяє надсилати відповіді поступово, і це корисно для великих наборів даних або тривалих процесів.
- Edge-функції: Розгортайте обробники маршрутів як Edge-функції для отримання відповідей з низькою затримкою ближче до ваших користувачів, використовуючи глобальні CDN.
- Спрощений дизайн API: Обробники маршрутів надають чистий та інтуїтивно зрозумілий API для обробки запитів та відповідей.
- Інтеграція з Server Actions: Тісна інтеграція з Server Actions забезпечує безперебійну комунікацію між вашими клієнтськими компонентами та серверною логікою.
Налаштування вашого проекту Next.js
Перш ніж занурюватися в обробники маршрутів, переконайтеся, що у вас налаштований проект Next.js з каталогом app
. Якщо ви починаєте новий проект, використовуйте таку команду:
npx create-next-app@latest my-nextjs-app
Під час процесу налаштування виберіть каталог app
, щоб увімкнути нову систему маршрутизації.
Створення вашого першого обробника маршрутів
Створімо просту кінцеву точку API, яка повертає відповідь у форматі JSON. Створіть новий каталог у каталозі app
, наприклад, /app/api/hello
. Усередині цього каталогу створіть файл з назвою route.ts
(або route.js
, якщо ви не використовуєте TypeScript).
Ось код вашого першого обробника маршрутів:
// app/api/hello/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Привіт від обробників маршрутів Next.js!' });
}
Пояснення:
import { NextResponse } from 'next/server';
: Імпортує об'єктNextResponse
, який використовується для створення відповідей API.export async function GET(request: Request) { ... }
: Визначає асинхронну функцію, яка обробляє GET-запити до кінцевої точки/api/hello
. Параметрrequest
надає доступ до об'єкта вхідного запиту.return NextResponse.json({ message: 'Привіт від обробників маршрутів Next.js!' });
: Створює JSON-відповідь з повідомленням і повертає її за допомогоюNextResponse.json()
.
Тепер ви можете отримати доступ до цієї кінцевої точки, перейшовши за адресою /api/hello
у вашому браузері або використовуючи інструменти, такі як curl
або Postman
.
Обробка різних HTTP-методів
Обробники маршрутів підтримують різні HTTP-методи, такі як GET, POST, PUT, DELETE, PATCH та OPTIONS. Ви можете визначити окремі функції для кожного методу в одному файлі route.ts
.
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Логіка для отримання всіх користувачів з бази даних
const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]; // Приклад даних
return NextResponse.json(users);
}
export async function POST(request: Request) {
const data = await request.json(); // Парсимо тіло запиту як JSON
// Логіка для створення нового користувача в базі даних з використанням 'data'
const newUser = { id: 3, name: data.name, email: data.email }; // Приклад
return NextResponse.json(newUser, { status: 201 }); // Повертаємо нового користувача зі статусом 201 Created
}
Пояснення:
- Функція
GET
отримує список користувачів (тут змодельовано) і повертає його як JSON-відповідь. - Функція
POST
парсить тіло запиту як JSON, створює нового користувача (змодельовано) і повертає нового користувача зі статусом 201 Created.
Доступ до даних запиту
Об'єкт request
надає доступ до різноманітної інформації про вхідний запит, включаючи заголовки, параметри запиту та тіло запиту.
Заголовки
Ви можете отримати доступ до заголовків запиту за допомогою властивості request.headers
:
export async function GET(request: Request) {
const userAgent = request.headers.get('user-agent');
console.log('User Agent:', userAgent);
return NextResponse.json({ userAgent });
}
Параметри запиту
Щоб отримати доступ до параметрів запиту, ви можете використовувати конструктор URL
:
export async function GET(request: Request) {
const url = new URL(request.url);
const searchParams = new URLSearchParams(url.search);
const id = searchParams.get('id');
console.log('ID:', id);
return NextResponse.json({ id });
}
Тіло запиту
Для запитів POST, PUT та PATCH ви можете отримати доступ до тіла запиту за допомогою методів request.json()
або request.text()
, залежно від типу контенту.
export async function POST(request: Request) {
const data = await request.json();
console.log('Data:', data);
return NextResponse.json({ receivedData: data });
}
Повернення відповідей
Об'єкт NextResponse
використовується для конструювання відповідей API. Він надає кілька методів для встановлення заголовків, кодів стану та тіла відповіді.
JSON-відповіді
Використовуйте метод NextResponse.json()
для повернення JSON-відповідей:
return NextResponse.json({ message: 'Success!', data: { name: 'John Doe' } }, { status: 200 });
Текстові відповіді
Використовуйте конструктор new Response()
для повернення відповідей у вигляді простого тексту:
return new Response('Hello, world!', { status: 200, headers: { 'Content-Type': 'text/plain' } });
Перенаправлення
Використовуйте NextResponse.redirect()
для перенаправлення користувачів на іншу URL-адресу:
import { redirect } from 'next/navigation';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.redirect(new URL('/new-location', request.url));
}
Встановлення заголовків
Ви можете встановлювати власні заголовки, використовуючи опцію headers
в NextResponse.json()
або new Response()
:
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'no-cache' } });
Інтеграція з проміжним ПЗ (Middleware)
Проміжне ПЗ (Middleware) дозволяє вам виконувати код до того, як запит буде оброблено вашим обробником маршрутів. Це корисно для автентифікації, авторизації, логування та інших наскрізних завдань.
Щоб створити проміжне ПЗ, створіть файл з назвою middleware.ts
(або middleware.js
) у каталозі app
або будь-якому його підкаталозі. Проміжне ПЗ буде застосовуватися до всіх маршрутів у цьому каталозі та його підкаталогах.
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/protected/:path*'], // Застосувати це проміжне ПЗ до шляхів, що починаються з /protected/
};
Пояснення:
- Функція
middleware
перевіряє наявність токена автентифікації в cookie запиту. - Якщо токен відсутній, вона перенаправляє користувача на сторінку входу.
- В іншому випадку, вона дозволяє запиту продовжити шлях до обробника маршрутів.
- Об'єкт
config
вказує, що це проміжне ПЗ має застосовуватися лише до маршрутів, що починаються з/protected/
.
Обробка помилок
Правильна обробка помилок є надзвичайно важливою для створення надійних API. Ви можете використовувати блоки try...catch
для обробки винятків і повернення відповідних відповідей про помилки.
export async function GET(request: Request) {
try {
// Симулюємо помилку
throw new Error('Щось пішло не так!');
} catch (error: any) {
console.error('Error:', error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
Пояснення:
- Блок
try...catch
перехоплює будь-які винятки, що виникають в обробнику маршрутів. - У блоці
catch
помилка логується, і повертається відповідь про помилку зі статусом 500 Internal Server Error.
Потокові відповіді
Обробники маршрутів підтримують потокові відповіді, що дозволяє надсилати дані клієнту поступово. Це особливо корисно для великих наборів даних або тривалих процесів.
import { Readable } from 'stream';
import { NextResponse } from 'next/server';
async function* generateData() {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Симулюємо затримку
yield `Частина даних ${i}\n`;
}
}
export async function GET(request: Request) {
const readableStream = Readable.from(generateData());
return new Response(readableStream, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
Пояснення:
- Функція
generateData
— це асинхронний генератор, який видає частини даних із затримкою. - Метод
Readable.from()
створює потік для читання з генератора. - Об'єкт
Response
створюється з потоком для читання як тіло, а заголовокContent-Type
встановлюється наtext/plain
.
Автентифікація та авторизація
Захист ваших кінцевих точок API є критично важливим. Ви можете реалізувати автентифікацію та авторизацію за допомогою проміжного ПЗ або безпосередньо у ваших обробниках маршрутів.
Автентифікація
Автентифікація перевіряє особу користувача, який робить запит. Поширені методи автентифікації включають:
- JWT (JSON Web Tokens): Генеруйте токен після успішного входу та перевіряйте його при наступних запитах.
- Автентифікація на основі сесій: Використовуйте cookie для зберігання ідентифікаторів сесій та перевіряйте їх при кожному запиті.
- OAuth: Делегуйте автентифікацію сторонньому провайдеру, такому як Google або Facebook.
Ось приклад автентифікації JWT за допомогою проміжного ПЗ:
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';
const secret = process.env.JWT_SECRET || 'ваш-секретний-ключ'; // Замініть на надійний, випадково згенерований секрет
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value;
if (!token) {
return NextResponse.json({ message: 'Потрібна автентифікація' }, { status: 401 });
}
try {
jwt.verify(token, secret);
return NextResponse.next();
} catch (error) {
return NextResponse.json({ message: 'Недійсний токен' }, { status: 401 });
}
}
export const config = {
matcher: ['/api/protected/:path*'],
};
Авторизація
Авторизація визначає, до яких ресурсів користувач має доступ. Зазвичай це базується на ролях або дозволах.
Ви можете реалізувати авторизацію у ваших обробниках маршрутів, перевіряючи ролі або дозволи користувача і повертаючи помилку, якщо він не має доступу.
// app/api/admin/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Припустимо, у вас є функція для отримання ролі користувача з токена або сесії
const userRole = await getUserRole(request);
if (userRole !== 'admin') {
return NextResponse.json({ message: 'Неавторизовано' }, { status: 403 });
}
// Логіка для отримання даних адміністратора
const adminData = { message: 'Дані адміністратора' };
return NextResponse.json(adminData);
}
async function getUserRole(request: Request): Promise {
// Замініть на вашу реальну логіку для вилучення ролі користувача із запиту
// Це може включати перевірку JWT токена або сесії
return 'admin'; // Приклад: жорстко закодована роль для демонстрації
}
Розгортання обробників маршрутів
Обробники маршрутів розгортаються як безсерверні функції на обраному вами хостинг-провайдері. Next.js підтримує різні платформи для розгортання, включаючи Vercel, Netlify, AWS та інші.
Для Vercel розгортання є простим: потрібно лише підключити ваш Git-репозиторій до Vercel і надіслати ваш код. Vercel автоматично визначає ваш проект Next.js і розгортає ваші обробники маршрутів як безсерверні функції.
Просунуті техніки
Edge-функції
Обробники маршрутів можна розгортати як Edge-функції, які виконуються на межі CDN, ближче до ваших користувачів. Це може значно зменшити затримку та покращити продуктивність.
Щоб розгорнути обробник маршрутів як Edge-функцію, додайте середовище виконання edge
до вашого файлу route.ts
:
export const runtime = 'edge';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Привіт з Edge!' });
}
Серверні дії (Server Actions)
Серверні дії дозволяють виконувати серверний код безпосередньо з ваших компонентів React. Обробники маршрутів та серверні дії бездоганно працюють разом, дозволяючи вам з легкістю створювати складні додатки.
Ось приклад використання серверної дії для виклику обробника маршрутів:
// app/components/MyComponent.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
async function handleSubmit(data: FormData) {
'use server';
const name = data.get('name');
const email = data.get('email');
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name, email }),
});
if (response.ok) {
router.refresh(); // Оновити сторінку, щоб відобразити зміни
}
}
export default function MyComponent() {
const router = useRouter();
return (
);
}
Кешування
Кешування може значно покращити продуктивність ваших кінцевих точок API. Ви можете використовувати заголовок Cache-Control
для контролю того, як ваші відповіді кешуються браузерами та CDN.
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'public, max-age=3600' } });
Цей приклад встановлює заголовок Cache-Control
на public, max-age=3600
, що вказує браузерам та CDN кешувати відповідь протягом однієї години.
Найкращі практики
- Використовуйте TypeScript: Використовуйте безпеку типів TypeScript для покращення якості коду та запобігання помилкам.
- Валідуйте запити: Перевіряйте вхідні запити для забезпечення цілісності даних та запобігання шкідливим даним.
- Обробляйте помилки коректно: Реалізуйте належну обробку помилок, щоб надавати клієнтам інформативні повідомлення про помилки.
- Захищайте свої кінцеві точки: Впроваджуйте автентифікацію та авторизацію для захисту ваших кінцевих точок API.
- Використовуйте проміжне ПЗ: Використовуйте проміжне ПЗ для наскрізних завдань, таких як автентифікація, логування та валідація запитів.
- Кешуйте відповіді: Використовуйте кешування для покращення продуктивності ваших кінцевих точок API.
- Моніторте ваші API: Здійснюйте моніторинг ваших API для швидкого виявлення та вирішення проблем.
- Документуйте ваші API: Документуйте ваші API, щоб іншим розробникам було легко їх використовувати. Розгляньте можливість використання таких інструментів, як Swagger/OpenAPI для документації API.
Приклади з реального світу
Ось кілька прикладів з реального світу, як можна використовувати обробники маршрутів:
- API для електронної комерції: Створення кінцевих точок API для керування товарами, замовленнями та користувачами.
- API для соціальних мереж: Створення кінцевих точок API для публікації твітів, підписки на користувачів та отримання стрічок новин.
- API для системи керування контентом (CMS): Створення кінцевих точок API для керування контентом, користувачами та налаштуваннями.
- API для аналітики даних: Створення кінцевих точок API для збору та аналізу даних. Наприклад, обробник маршрутів може отримувати дані з пікселів відстеження на різних веб-сайтах і агрегувати інформацію для звітності.
Приклад міжнародної електронної комерції: Обробник маршрутів, що використовується для отримання цін на товари залежно від країни користувача. Кінцева точка може використовувати геолокацію запиту (отриману з IP-адреси) для визначення місцезнаходження користувача та повернення цін у відповідній валюті. Це сприяє локалізованому досвіду покупок.
Приклад глобальної автентифікації: Обробник маршрутів, що реалізує багатофакторну автентифікацію (MFA) для користувачів у всьому світі. Це може включати надсилання SMS-кодів або використання додатків-автентифікаторів, з дотриманням правил конфіденційності та телекомунікаційних інфраструктур різних регіонів.
Доставка багатомовного контенту: Обробник маршрутів, що доставляє контент бажаною для користувача мовою. Її можна визначити за заголовком `Accept-Language` у запиті. Цей приклад підкреслює необхідність правильного кодування UTF-8 та підтримки мов з написанням справа наліво, де це доречно.
Висновок
Обробники маршрутів Next.js надають потужний і гнучкий спосіб створення кінцевих точок API безпосередньо у вашому додатку Next.js. Використовуючи обробники маршрутів, ви можете з легкістю створювати надійні API, розміщувати свою бекенд-логіку поруч із компонентами React та використовувати такі функції, як проміжне ПЗ, потокова передача та Edge-функції.
Цей вичерпний посібник охопив усе, від базового налаштування до просунутих технік. Дотримуючись найкращих практик, викладених у цьому посібнику, ви зможете створювати високоякісні API, які є безпечними, продуктивними та легкими в підтримці.