API Routes (Route Handlers) в Next.js

Route Handlers (в Pages Router назывались API Routes) позволяют создавать серверные API-эндпоинты прямо в Next.js приложении. Они определяются в файлах route.ts внутри директории app.

Базовый пример

// app/api/problems/route.ts
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'

export async function GET() {
  const problems = await db.problem.findMany()
  return NextResponse.json(problems)
}

export async function POST(request: Request) {
  const body = await request.json()

  const problem = await db.problem.create({
    data: body
  })

  return NextResponse.json(problem, { status: 201 })
}

Каждый экспортированный метод (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) обрабатывает соответствующий HTTP-запрос.

Динамические Route Handlers

// app/api/problems/[id]/route.ts
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'

export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  const problem = await db.problem.findUnique({
    where: { id: params.id }
  })

  if (!problem) {
    return NextResponse.json(
      { error: 'Problem not found' },
      { status: 404 }
    )
  }

  return NextResponse.json(problem)
}

export async function DELETE(
  request: Request,
  { params }: { params: { id: string } }
) {
  await db.problem.delete({ where: { id: params.id } })
  return new Response(null, { status: 204 })
}

Работа с Request

Route Handlers используют стандартный Web API Request:

// app/api/search/route.ts
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const query = searchParams.get('q')

  if (!query) {
    return NextResponse.json(
      { error: 'Query parameter required' },
      { status: 400 }
    )
  }

  const results = await db.problem.findMany({
    where: { name: { contains: query, mode: 'insensitive' } }
  })

  return NextResponse.json(results)
}

Чтение заголовков и cookies

import { cookies, headers } from 'next/headers'

export async function GET() {
  const cookieStore = cookies()
  const token = cookieStore.get('session')

  const headersList = headers()
  const userAgent = headersList.get('user-agent')

  return NextResponse.json({ token, userAgent })
}

Кеширование

GET-запросы без чтения Request кешируются по умолчанию:

// Кешируется (статический Route Handler)
export async function GET() {
  const data = await fetch('https://api.example.com/data')
  return NextResponse.json(data)
}

// Не кешируется (читаем Request)
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  // ...
}

Для явного управления кешем:

export const dynamic = 'force-dynamic' // всегда динамический
export const revalidate = 300          // ревалидация через 5 минут

Route Handlers vs Server Actions

Route HandlersServer Actions
НазначениеAPI-эндпоинтыМутации из UI
HTTP-методыGET, POST, PUT, DELETE...Только POST
Вызов изЛюбой клиент (fetch, curl)Только из компонентов
Progressive enhancementНетДа (формы)

Когда что использовать:

Используй Server Actions для мутаций данных из интерфейса (формы, кнопки). Используй Route Handlers для публичных API, webhook'ов, интеграций с внешними сервисами и случаев, когда нужны конкретные HTTP-методы.

Полезные ресурсы

Связанные темы