В этом руководстве рассказывается, как создавать API с помощью Next.js, включая настройку проекта, работу с App Router и Route Handlers, обработку нескольких HTTP-методов, реализацию динамической маршрутизации, создание переиспользуемой middleware-логики и принятие решений о необходимости выделенного API-слоя.
- 1. Начало работы
- 2. Зачем (и когда) создавать API с Next.js
- 3. Создание API с Route Handlers
- 4. Работа с Web API
- 5. Динамические маршруты
- 6. Использование Next.js в качестве прокси или промежуточного слоя
- 7. Создание переиспользуемой "middleware"-логики
- 8. Деплой и особенности "SPA Mode"
- 9. Когда можно не создавать API-эндпоинт
- 10. Собираем всё вместе
- Заключение
- Часто задаваемые вопросы
1. Начало работы
1.1 Создание Next.js приложения
Если вы начинаете с нуля, создайте новый проект Next.js с помощью:
npx create-next-app@latest --api
Примечание: Флаг
--api
автоматически включает примерroute.ts
в папкеapp/
вашего нового проекта, демонстрируя создание API-эндпоинта.
1.2 App Router против Pages Router
- Pages Router: Исторически Next.js использовал
pages/api/*
для API. Этот подход опирался на объекты запроса/ответа Node.js и API, похожее на Express. - App Router (по умолчанию): Представленный в Next.js 13, App Router полностью использует стандартные Web API Request/Response. Вместо
pages/api/*
теперь можно размещать файлыroute.ts
илиroute.js
в любом месте директорииapp/
.
Почему стоит переключиться? "Route Handlers" в App Router основаны на Web Platform Request/Response APIs, а не на специфичных для Node.js API. Это упрощает обучение, уменьшает трение и позволяет повторно использовать знания в разных инструментах.
2. Зачем (и когда) создавать API с Next.js
-
Публичный API для нескольких клиентов
- Можно создать публичный API, который будет использоваться вашим Next.js веб-приложением, отдельным мобильным приложением или любым сторонним сервисом. Например, вы можете получать данные из /api/users как в React-сайте, так и в React Native мобильном приложении.
-
Прокси к существующему бэкенду
- Иногда нужно скрыть или объединить внешние микросервисы за единым эндпоинтом. Route Handlers в Next.js могут выступать в роли прокси или промежуточного слоя для другого существующего бэкенда. Например, можно перехватывать запросы, обрабатывать аутентификацию, преобразовывать данные и затем передавать запрос вышестоящему API.
-
Вебхуки и интеграции
- Если вы получаете внешние колбэки или вебхуки (например, от Stripe, GitHub, Twilio), их можно обрабатывать с помощью Route Handlers.
-
Кастомная аутентификация
- Если нужны сессии, токены или другая логика аутентификации, можно хранить куки, читать заголовки и отвечать соответствующими данными в API-слое Next.js.
Примечание: Если вам нужно только серверное получение данных для вашего Next.js приложения (и не требуется делиться этими данными внешне), Server Components может быть достаточно для получения данных непосредственно во время рендеринга — отдельный API-слой не требуется.
3. Создание API с Route Handlers
3.1 Базовая настройка файлов
В App Router (app/
) создайте папку, представляющую ваш маршрут, и внутри неё файл route.ts
.
Например, для создания эндпоинта на /api/users
:
app
└── api
└── users
└── route.ts
3.2 Несколько HTTP-методов в одном файле
В отличие от API-маршрутов в Pages Router (где был один экспорт по умолчанию), из одного файла можно экспортировать несколько функций, представляющих разные HTTP-методы.
export async function GET(request: Request) {
// Например, здесь можно получить данные из БД
const users = [
{ id: 1, name: 'Алиса' },
{ id: 2, name: 'Боб' }
];
return new Response(JSON.stringify(users), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
export async function POST(request: Request) {
// Разбираем тело запроса
const body = await request.json();
const { name } = body;
// Например, вставляем нового пользователя в БД
const newUser = { id: Date.now(), name };
return new Response(JSON.stringify(newUser), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
}
Теперь GET-запрос к /api/users
возвращает список пользователей, а POST-запрос на тот же URL добавляет нового пользователя.
4. Работа с Web API
4.1 Прямое использование Request & Response
По умолчанию методы Route Handler (GET
, POST
и т.д.) получают стандартный объект Request, и вы должны возвращать стандартный объект Response.
4.2 Параметры запроса
import { NextRequest } from 'next/server';
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const query = searchParams.get('query'); // например, `/api/search?query=hello`
return new Response(
JSON.stringify({ result: `Вы искали: ${query}` }),
{
headers: { 'Content-Type': 'application/json' },
},
);
}
4.3 Заголовки и куки
import { NextRequest } from 'next/server';
import { cookies, headers } from 'next/headers';
export async function GET(request: NextRequest) {
// 1. Использование помощников из 'next/headers'
const cookieStore = await cookies();
const token = cookieStore.get('token');
const headersList = await headers();
const referer = headersList.get('referer');
// 2. Использование стандартных Web API
const userAgent = request.headers.get('user-agent');
return new Response(JSON.stringify({ token, referer, userAgent }), {
headers: { 'Content-Type': 'application/json' },
});
}
Функции cookies()
и headers()
могут быть полезны, если вы планируете повторно использовать общую логику в другом серверном коде Next.js. Обратите внимание, что Next.js также предоставляет NextRequest
и NextResponse
, которые расширяют базовые Web API.
5. Динамические маршруты
Для создания динамических путей (например, /api/users/:id
) используйте Dynamic Segments в структуре папок:
app
└── api
└── users
└── [id]
└── route.ts
Этот файл соответствует URL типа /api/users/123
, где 123
захватывается как параметр.
import { NextRequest } from 'next/server';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const id = (await params).id;
// Например, запрос пользователя с ID `id` из БД
return new Response(JSON.stringify({ id, name: `Пользователь ${id}` }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const id = (await params).id;
// Например, удаление пользователя с ID `id` из БД
return new Response(null, { status: 204 });
}
Здесь params.id
даёт вам динамический сегмент.
6. Использование Next.js в качестве прокси или промежуточного слоя
Распространённый сценарий — проксирование существующего бэкенд-сервиса. Можно аутентифицировать запросы, вести логирование или преобразовывать данные перед отправкой на удалённый сервер или бэкенд:
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const response = await fetch('https://example.com/api/data', {
// Опционально: передача заголовков, добавление токенов аутентификации и т.д.
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
// Преобразование или передача ответа
const data = await response.json();
const transformed = { ...data, source: 'проксировано-через-nextjs' };
return new Response(JSON.stringify(transformed), {
headers: { 'Content-Type': 'application/json' },
});
}
Теперь клиентам нужно только вызывать /api/external
, а Next.js обработает остальное. Это иногда называют "Backend for Frontend" или BFF.
7. Создание переиспользуемой "middleware"-логики
Если нужно применить одну и ту же логику (например, проверки аутентификации, логирование) к нескольким Route Handlers, можно создать переиспользуемые функции, оборачивающие ваши обработчики:
import { NextRequest } from 'next/server';
type Handler = (req: NextRequest, context?: any) => Promise<Response>;
export function withAuth(handler: Handler): Handler {
return async (req, context) => {
const token = req.cookies.get('token')?.value;
if (!token) {
return new Response(JSON.stringify({ error: 'Не авторизован' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
// Если аутентификация прошла, вызываем оригинальный обработчик
return handler(req, context);
};
}
Затем в вашем Route Handler:
import { NextRequest } from 'next/server';
import { withAuth } from '@/lib/with-auth';
async function secretGET(request: NextRequest) {
return new Response(JSON.stringify({ secret: 'Здесь водятся драконы' }), {
headers: { 'Content-Type': 'application/json' },
});
}
export const GET = withAuth(secretGET);
8. Деплой и особенности "SPA Mode"
8.1 Стандартный деплой на Node.js
Стандартный деплой Next.js сервера с использованием next start
позволяет использовать такие функции, как Route Handlers, Server Components, Middleware и другие — с возможностью работы с динамической информацией во время запроса.
Дополнительная конфигурация не требуется. Подробнее см. в разделе Деплой.
8.2 SPA/Статический экспорт
Next.js также поддерживает экспорт всего сайта как статического Single-Page Application (SPA).
Это можно включить, установив:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'export',
};
export default nextConfig;
В режиме статического экспорта Next.js генерирует чисто статические HTML, CSS и JS. Нельзя выполнять серверный код (например, API-эндпоинты). Если API всё ещё нужен, его придётся размещать отдельно (например, на отдельном Node.js сервере).
Примечание:
- GET Route Handlers могут быть статически экспортированы, если они не зависят от динамических данных запроса. Они становятся статическими файлами в папке
out
.- Все остальные серверные функции (динамические запросы, перезапись куки и т.д.) не поддерживаются в чистом SPA экспорте.
8.3 Развертывание API на Vercel
Если вы развертываете свое Next.js-приложение на Vercel, у нас есть руководство по развертыванию API. Оно включает другие функции Vercel, такие как программное ограничение частоты запросов (rate-limiting) через Vercel Firewall. Vercel также предлагает Cron Jobs, которые часто требуются при работе с API.
9. Когда можно не создавать конечную точку API
С React Server Components в App Router вы можете получать данные напрямую на сервере, не создавая публичную конечную точку:
// (Server Component)
export default async function UsersPage() {
// Этот fetch выполняется на сервере (клиентский код здесь не нужен)
const res = await fetch('https://api.example.com/users');
const data = await res.json();
return (
<main>
<h1>Пользователи</h1>
<ul>
{data.map((user: any) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</main>
);
}
Если ваши данные используются только внутри вашего Next.js-приложения, вам может вообще не понадобиться публичное API.
10. Собираем все вместе
- Создайте новый проект Next.js:
npx create-next-app@latest --api
. - Добавьте Route Handlers в директорию
app/
(например,app/api/users/route.ts
). - Экспортируйте HTTP-методы (
GET
,POST
,PUT
,DELETE
и т.д.) в том же файле. - Используйте стандартные веб-API для работы с объектом
Request
и возвратаResponse
. - Создайте публичное API, если вам нужно, чтобы другие клиенты использовали ваши данные, или для проксирования серверного сервиса.
- Получайте данные из новых API-маршрутов на клиенте (например, внутри Client Component или с помощью
fetch('/api/...')
). - Или вообще не создавайте API, если Server Component может просто получать данные.
- Добавьте общий шаблон "middleware" (например,
withAuth()
) для аутентификации или другой повторяющейся логики. - Разверните в среде с поддержкой Node.js для серверных функций или экспортируйте статически, если вам нужно только статическое SPA.
Заключение
Использование App Router и Route Handlers в Next.js дает вам гибкий, современный способ создания API, напрямую использующих Web Platform. Вы можете:
- Создать полноценное публичное API для использования веб-, мобильными или сторонними клиентами.
- Проксировать и настраивать вызовы существующих внешних сервисов.
- Реализовать повторно используемый слой "middleware" для аутентификации, логирования или любой повторяющейся логики.
- Динамически маршрутизировать запросы с помощью структуры папок
[id]
.
Часто задаваемые вопросы
А как насчет Server Actions?
Можно рассматривать Server Actions как автоматически генерируемые POST
API-маршруты, которые можно вызывать с клиента.
Они предназначены для операций изменения данных, таких как создание, обновление или удаление. Вы вызываете Server Action как обычную JavaScript-функцию, вместо явного fetch
к определенному API-маршруту.
Хотя сетевой запрос все равно происходит, вам не нужно управлять им явно. URL-путь генерируется автоматически и шифруется, поэтому вы не можете вручную обратиться к маршруту типа /api/users
в браузере.
Если вы планируете использовать Server Actions и предоставлять публичное API, мы рекомендуем вынести основную логику в Data Access Layer и вызывать одну и ту же логику как из Server Action, так и из API-маршрута.
Можно ли использовать TypeScript с Route Handlers?
Да, вы можете использовать TypeScript с Route Handlers. Например, определяя типы Request
и Response
в вашем файле route
.
Подробнее о TypeScript в Next.js.
Какие лучшие практики для аутентификации?
Узнайте больше в нашей документации по аутентификации.