Route Handler

Route Handler ermöglichen es Ihnen, benutzerdefinierte Anfragehandler für eine bestimmte Route mit den Web-Request- und Response-APIs zu erstellen.

Route.js Special File

Wichtig zu wissen: Route Handler sind nur innerhalb des app-Verzeichnisses verfügbar. Sie entsprechen den API-Routen innerhalb des pages-Verzeichnisses, was bedeutet, dass Sie API-Routen und Route Handler nicht zusammen verwenden müssen.

Konvention

Route Handler werden in einer route.js|ts-Datei innerhalb des app-Verzeichnisses definiert:

export const dynamic = 'force-dynamic' // Standardwert ist auto
export async function GET(request: Request) {}
export const dynamic = 'force-dynamic' // Standardwert ist auto
export async function GET(request) {}

Route Handler können ähnlich wie page.js und layout.js innerhalb des app-Verzeichnisses verschachtelt werden. Es darf jedoch keine route.js-Datei auf demselben Routensegment-Level wie page.js geben.

Unterstützte HTTP-Methoden

Die folgenden HTTP-Methoden werden unterstützt: GET, POST, PUT, PATCH, DELETE, HEAD und OPTIONS. Wenn eine nicht unterstützte Methode aufgerufen wird, gibt Next.js eine 405 Method Not Allowed-Antwort zurück.

Erweiterte NextRequest- und NextResponse-APIs

Neben der Unterstützung der nativen Request- und Response-APIs erweitert Next.js diese mit NextRequest und NextResponse, um praktische Hilfsmittel für erweiterte Anwendungsfälle bereitzustellen.

Verhalten

Caching

Route Handler werden standardmäßig zwischengespeichert, wenn die GET-Methode mit dem Response-Objekt verwendet wird.

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const data = await res.json()

  return Response.json({ data })
}
export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const data = await res.json()

  return Response.json({ data })
}

TypeScript-Warnung: Response.json() ist erst ab TypeScript 5.2 gültig. Wenn Sie eine ältere TypeScript-Version verwenden, können Sie stattdessen NextResponse.json() für typisierte Antworten verwenden.

Opt-out vom Caching

Sie können das Caching deaktivieren durch:

Beispiel:

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const id = searchParams.get('id')
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY!,
    },
  })
  const product = await res.json()

  return Response.json({ product })
}
export async function GET(request) {
  const { searchParams } = new URL(request.url)
  const id = searchParams.get('id')
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const product = await res.json()

  return Response.json({ product })
}

Ebenso führt die POST-Methode dazu, dass der Route Handler dynamisch ausgewertet wird.

export async function POST() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY!,
    },
    body: JSON.stringify({ time: new Date().toISOString() }),
  })

  const data = await res.json()

  return Response.json(data)
}
export async function POST() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
    body: JSON.stringify({ time: new Date().toISOString() }),
  })

  const data = await res.json()

  return Response.json(data)
}

Wichtig zu wissen: Wie API-Routen können Route Handler für Fälle wie die Verarbeitung von Formulareingaben verwendet werden. Eine neue Abstraktion für die Verarbeitung von Formularen und Mutationen, die eng mit React integriert ist, wird derzeit entwickelt.

Routenauflösung

Sie können sich eine route als die grundlegendste Routing-Primitive vorstellen.

  • Sie nehmen nicht an Layouts oder clientseitigen Navigationen wie page teil.
  • Es darf keine route.js-Datei auf derselben Route wie page.js geben.
SeiteRouteErgebnis
app/page.jsapp/route.jsCross Icon Konflikt
app/page.jsapp/api/route.jsCheck Icon Gültig
app/[user]/page.jsapp/api/route.jsCheck Icon Gültig

Jede route.js- oder page.js-Datei übernimmt alle HTTP-Verben für diese Route.

app/page.js
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

// ❌ Konflikt
// `app/route.js`
export async function POST(request) {}

Beispiele

Die folgenden Beispiele zeigen, wie Route Handler mit anderen Next.js-APIs und Funktionen kombiniert werden können.

Neuvalidierung zwischengespeicherter Daten

Sie können zwischengespeicherte Daten neu validieren mit der next.revalidate-Option:

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    next: { revalidate: 60 }, // Alle 60 Sekunden neu validieren
  })
  const data = await res.json()

  return Response.json(data)
}
export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    next: { revalidate: 60 }, // Alle 60 Sekunden neu validieren
  })
  const data = await res.json()

  return Response.json(data)
}

Alternativ können Sie die revalidate-Segmentkonfigurationsoption verwenden:

export const revalidate = 60

Dynamische Funktionen

Route Handler können mit dynamischen Funktionen von Next.js verwendet werden, wie cookies und headers.

Cookies

Sie können Cookies mit cookies aus next/headers lesen oder setzen. Diese Serverfunktion kann direkt in einem Route Handler aufgerufen oder in einer anderen Funktion verschachtelt werden.

Alternativ können Sie eine neue Response mit dem Set-Cookie-Header zurückgeben.

import { cookies } from 'next/headers'

export async function GET(request: Request) {
  const cookieStore = cookies()
  const token = cookieStore.get('token')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { 'Set-Cookie': `token=${token.value}` },
  })
}
import { cookies } from 'next/headers'

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

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { 'Set-Cookie': `token=${token}` },
  })
}

Sie können auch die zugrunde liegenden Web-APIs verwenden, um Cookies aus der Anfrage (NextRequest) zu lesen:

import { type NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
  const token = request.cookies.get('token')
}
export async function GET(request) {
  const token = request.cookies.get('token')
}

Sie können Header mit headers aus next/headers lesen. Diese Serverfunktion kann direkt in einem Route Handler aufgerufen oder in einer anderen Funktion verschachtelt werden.

Diese headers-Instanz ist schreibgeschützt. Um Header zu setzen, müssen Sie eine neue Response mit neuen headers zurückgeben.

import { headers } from 'next/headers'

export async function GET(request: Request) {
  const headersList = headers()
  const referer = headersList.get('referer')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { referer: referer },
  })
}
import { headers } from 'next/headers'

export async function GET(request) {
  const headersList = headers()
  const referer = headersList.get('referer')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { referer: referer },
  })
}

Sie können auch die zugrunde liegenden Web-APIs verwenden, um Header aus der Anfrage (NextRequest) zu lesen:

import { type NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
  const requestHeaders = new Headers(request.headers)
}
export async function GET(request) {
  const requestHeaders = new Headers(request.headers)
}

Weiterleitungen

import { redirect } from 'next/navigation'

export async function GET(request: Request) {
  redirect('https://nextjs.org/')
}
import { redirect } from 'next/navigation'

export async function GET(request) {
  redirect('https://nextjs.org/')
}

Dynamische Routensegmente

Wir empfehlen, die Seite Defining Routes zu lesen, bevor Sie fortfahren.

Route Handler können Dynamische Segmente verwenden, um Anfragehandler aus dynamischen Daten zu erstellen.

export async function GET(
  request: Request,
  { params }: { params: { slug: string } }
) {
  const slug = params.slug // 'a', 'b' oder 'c'
}
export async function GET(request, { params }) {
  const slug = params.slug // 'a', 'b' oder 'c'
}
RouteBeispiel-URLparams
app/items/[slug]/route.js/items/a{ slug: 'a' }
app/items/[slug]/route.js/items/b{ slug: 'b' }
app/items/[slug]/route.js/items/c{ slug: 'c' }

URL-Abfrageparameter

Das an den Route Handler übergebene Anfrageobjekt ist eine NextRequest-Instanz, die einige zusätzliche Hilfsmethoden bietet, einschließlich der einfacheren Handhabung von Abfrageparametern.

import { type NextRequest } from 'next/server'

export function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams
  const query = searchParams.get('query')
  // query ist "hello" für /api/search?query=hello
}
export function GET(request) {
  const searchParams = request.nextUrl.searchParams
  const query = searchParams.get('query')
  // query ist "hello" für /api/search?query=hello
}

Streaming

Streaming wird häufig in Kombination mit Large Language Models (LLMs) wie OpenAI für KI-generierte Inhalte verwendet. Erfahren Sie mehr über das AI SDK.

import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

export const runtime = 'edge'

export async function POST(req: Request) {
  const { messages } = await req.json()
  const response = await openai.chat.completions.create({
    model: 'gpt-3.5-turbo',
    stream: true,
    messages,
  })

  const stream = OpenAIStream(response)

  return new StreamingTextResponse(stream)
}
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

export const runtime = 'edge'

export async function POST(req) {
  const { messages } = await req.json()
  const response = await openai.chat.completions.create({
    model: 'gpt-3.5-turbo',
    stream: true,
    messages,
  })

  const stream = OpenAIStream(response)

  return new StreamingTextResponse(stream)
}

Diese Abstraktionen verwenden die Web-APIs, um einen Stream zu erstellen. Sie können auch die zugrunde liegenden Web-APIs direkt verwenden.

// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator: any) {
  return new ReadableStream({
    async pull(controller) {
      const { value, done } = await iterator.next()

      if (done) {
        controller.close()
      } else {
        controller.enqueue(value)
      }
    },
  })
}

function sleep(time: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

const encoder = new TextEncoder()

async function* makeIterator() {
  yield encoder.encode('<p>One</p>')
  await sleep(200)
  yield encoder.encode('<p>Two</p>')
  await sleep(200)
  yield encoder.encode('<p>Three</p>')
}

export async function GET() {
  const iterator = makeIterator()
  const stream = iteratorToStream(iterator)

  return new Response(stream)
}
// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator) {
  return new ReadableStream({
    async pull(controller) {
      const { value, done } = await iterator.next()

      if (done) {
        controller.close()
      } else {
        controller.enqueue(value)
      }
    },
  })
}

function sleep(time) {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

const encoder = new TextEncoder()

async function* makeIterator() {
  yield encoder.encode('<p>One</p>')
  await sleep(200)
  yield encoder.encode('<p>Two</p>')
  await sleep(200)
  yield encoder.encode('<p>Three</p>')
}

export async function GET() {
  const iterator = makeIterator()
  const stream = iteratorToStream(iterator)

  return new Response(stream)
}

Request Body

Sie können den Request-Body mit den standardmäßigen Web-API-Methoden lesen:

export async function POST(request: Request) {
  const res = await request.json()
  return Response.json({ res })
}
export async function POST(request) {
  const res = await request.json()
  return Response.json({ res })
}

Request Body FormData

Sie können FormData mit der Funktion request.formData() lesen:

export async function POST(request: Request) {
  const formData = await request.formData()
  const name = formData.get('name')
  const email = formData.get('email')
  return Response.json({ name, email })
}
export async function POST(request) {
  const formData = await request.formData()
  const name = formData.get('name')
  const email = formData.get('email')
  return Response.json({ name, email })
}

Da formData-Daten alle als Zeichenketten vorliegen, können Sie zod-form-data verwenden, um die Anfrage zu validieren und Daten im gewünschten Format (z.B. number) zu erhalten.

CORS

Sie können CORS-Header für einen bestimmten Route Handler mit den standardmäßigen Web-API-Methoden setzen:

export const dynamic = 'force-dynamic' // defaults to auto

export async function GET(request: Request) {
  return new Response('Hello, Next.js!', {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}
export const dynamic = 'force-dynamic' // defaults to auto

export async function GET(request) {
  return new Response('Hello, Next.js!', {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}

Gut zu wissen:

Webhooks

Sie können einen Route Handler verwenden, um Webhooks von Drittanbietern zu empfangen:

export async function POST(request: Request) {
  try {
    const text = await request.text()
    // Webhook-Payload verarbeiten
  } catch (error) {
    return new Response(`Webhook error: ${error.message}`, {
      status: 400,
    })
  }

  return new Response('Success!', {
    status: 200,
  })
}
export async function POST(request) {
  try {
    const text = await request.text()
    // Webhook-Payload verarbeiten
  } catch (error) {
    return new Response(`Webhook error: ${error.message}`, {
      status: 400,
    })
  }

  return new Response('Success!', {
    status: 200,
  })
}

Anders als bei API-Routen mit dem Pages Router benötigen Sie keine zusätzliche Konfiguration wie bodyParser.

Edge- und Node.js-Runtimes

Route Handler bieten eine isomorphe Web-API, die nahtlos sowohl Edge- als auch Node.js-Runtimes unterstützt, inklusive Streaming. Da Route Handler dieselbe Route-Segment-Konfiguration wie Seiten und Layouts verwenden, unterstützen sie lang erwartete Funktionen wie allgemein nutzbare statisch regenerierte Route Handler.

Sie können die runtime-Segmentkonfiguration verwenden, um die Laufzeitumgebung anzugeben:

export const runtime = 'edge' // Standard ist 'nodejs'

Nicht-UI-Antworten

Sie können Route Handler verwenden, um Nicht-UI-Inhalte zurückzugeben. Beachten Sie, dass sitemap.xml, robots.txt, App-Icons und Open-Graph-Bilder bereits integrierte Unterstützung bieten.

export const dynamic = 'force-dynamic' // defaults to auto

export async function GET() {
  return new Response(
    `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">

<channel>
  <title>Next.js Documentation</title>
  <link>https://nextjs.org/docs</link>
  <description>The React Framework for the Web</description>
</channel>

</rss>`,
    {
      headers: {
        'Content-Type': 'text/xml',
      },
    }
  )
}
export const dynamic = 'force-dynamic' // defaults to auto

export async function GET() {
  return new Response(`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">

<channel>
  <title>Next.js Documentation</title>
  <link>https://nextjs.org/docs</link>
  <description>The React Framework for the Web</description>
</channel>

</rss>`)
}

Segment-Konfigurationsoptionen

Route Handler verwenden dieselbe Route-Segment-Konfiguration wie Seiten und Layouts.

export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'

Weitere Details finden Sie in der API-Referenz.