API Routes
API-роуты в App Router создаются файлами route.ts в папках app/api/.
Базовый роут
app/api/hello/route.ts:
import { NextResponse } from 'next/server';
export async function GET() {
return NextResponse.json({ message: 'Hello' });
}
URL: /api/hello.
Методы
export async function GET(request: Request) {}
export async function POST(request: Request) {}
export async function PUT(request: Request) {}
export async function PATCH(request: Request) {}
export async function DELETE(request: Request) {}
Каждая функция — свой HTTP-метод.
Чтение запроса
export async function POST(request: Request) {
const body = await request.json();
const header = request.headers.get('authorization');
const { searchParams } = new URL(request.url);
const page = searchParams.get('page');
return NextResponse.json({ body, page });
}
Динамические роуты
app/api/posts/[id]/route.ts:
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const post = await getPost(id);
return NextResponse.json(post);
}
Ответы
// JSON
return NextResponse.json({ data });
return NextResponse.json({ error: 'Not found' }, { status: 404 });
// Пустой
return new Response(null, { status: 204 });
// Текст
return new Response('Hello', { headers: { 'Content-Type': 'text/plain' } });
// Редирект
return NextResponse.redirect(new URL('/login', request.url));
Заголовки
return NextResponse.json(data, {
headers: {
'Cache-Control': 'public, max-age=3600',
'X-Custom': 'value',
},
});
CORS
return NextResponse.json(data, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST',
'Access-Control-Allow-Headers': 'Content-Type',
},
});
// OPTIONS
return new Response(null, { status: 204, headers });
Валидация
import { z } from 'zod';
const schema = z.object({
title: z.string().min(3),
body: z.string().min(10),
});
export async function POST(request: Request) {
const json = await request.json();
const result = schema.safeParse(json);
if (!result.success) {
return NextResponse.json(
{ errors: result.error.flatten() },
{ status: 422 }
);
}
const post = await db.post.create({ data: result.data });
return NextResponse.json(post, { status: 201 });
}
Авторизация
import { cookies } from 'next/headers';
export async function GET() {
const cookieStore = await cookies();
const token = cookieStore.get('token')?.value;
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const user = await verifyToken(token);
return NextResponse.json({ user });
}
Работа с БД
import { db } from '@/lib/db';
export async function GET() {
const posts = await db.post.findMany();
return NextResponse.json(posts);
}
Streaming
export async function GET() {
const stream = new ReadableStream({
async start(controller) {
for (let i = 0; i < 10; i++) {
controller.enqueue(`chunk ${i}\n`);
await new Promise((r) => setTimeout(r, 100));
}
controller.close();
},
});
return new Response(stream, {
headers: { 'Content-Type': 'text/plain' },
});
}
Когда использовать
- Webhooks — от внешних сервисов
- Публичные API — для мобильных приложений
- Интеграции — с другими сервисами
- SSE и стримы — real-time
Для внутренних мутаций — Server Actions, они проще.
Итоги
route.tsвapp/api/- Экспорт
GET,POST, ... NextResponse.jsonдля ответа- Параметры через
params - Zod для валидации
- Server Actions для внутренних мутаций