Route Handler

Route Handler ermöglichen es Ihnen, benutzerdefinierte Request-Handler 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 sind das Äquivalent zu API Routes innerhalb des pages-Verzeichnisses, was bedeutet, dass Sie nicht API Routes und Route Handler zusammen verwenden müssen.

Konvention

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

export async function GET(request: Request) {}
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 Routen-Segment-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 fortgeschrittene 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 niedrigere 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:

  • Verwendung des Request-Objekts mit der GET-Methode.
  • Verwendung einer anderen HTTP-Methode.
  • Verwendung von Dynamischen Funktionen wie cookies und headers.
  • Manuelle Angabe des dynamischen Modus durch die Segment Config Options.

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 Routes können Route Handler für Fälle wie die Verarbeitung von Formularübermittlungen verwendet werden. Eine neue Abstraktion für die Verarbeitung von Formularen und Mutationen, die tief mit React integriert ist, wird derzeit entwickelt.

Routen-Auflösung

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

  • Sie nehmen nicht an Layouts oder clientseitigen Navigationen wie page teil.
  • Es kann 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.

Revalidierung zwischengespeicherter Daten

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

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    next: { revalidate: 60 }, // Alle 60 Sekunden revalidieren
  })
  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 revalidieren
  })
  const data = await res.json()

  return Response.json(data)
}

Alternativ können Sie die revalidate Segment Config Option 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. Diese Serverfunktion kann direkt in einem Route Handler aufgerufen werden oder in einer anderen Funktion verschachtelt sein.

Diese cookies-Instanz ist schreibgeschützt. Um Cookies zu setzen, müssen 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}` },
  })
}

Alternativ können Sie Abstraktionen über den zugrunde liegenden Web-APIs verwenden, um Cookies zu lesen (NextRequest):

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')
}

Headers

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

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 },
  })
}

Alternativ können Sie Abstraktionen über den zugrunde liegenden Web-APIs verwenden, um Header zu lesen (NextRequest):

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)
}

Redirects

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 Routen-Segmente

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

Route Handler können Dynamische Segmente verwenden, um Request-Handler 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 Query-Parameter

Das Request-Objekt, das an den Route Handler übergeben wird, ist eine NextRequest-Instanz, die einige zusätzliche Hilfsmethoden bietet, einschließlich der einfacheren Handhabung von Query-Parametern.

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 großen Sprachmodellen (Large Language Models, LLMs) wie OpenAI für KI-generierte Inhalte verwendet. Weitere Informationen finden Sie im AI SDK.

import { Configuration, OpenAIApi } from 'openai-edge'
import { OpenAIStream, StreamingTextResponse } from 'ai'

export const runtime = 'edge'

const apiConfig = new Configuration({
  apiKey: process.env.OPENAI_API_KEY!,
})

const openai = new OpenAIApi(apiConfig)

export async function POST(req: Request) {
  // Extrahiere die `messages` aus dem Body der Anfrage
  const { messages } = await req.json()

  // Fordere die OpenAI-API für die Antwort basierend auf dem Prompt an
  const response = await openai.createChatCompletion({
    model: 'gpt-3.5-turbo',
    stream: true,
    messages: messages,
    max_tokens: 500,
    temperature: 0.7,
    top_p: 1,
    frequency_penalty: 1,
    presence_penalty: 1,
  })

  // Konvertiere die Antwort in einen nutzerfreundlichen Text-Stream
  const stream = OpenAIStream(response)

  // Antworte mit dem Stream
  return new StreamingTextResponse(stream)
}
import { Configuration, OpenAIApi } from 'openai-edge'
import { OpenAIStream, StreamingTextResponse } from 'ai'

export const runtime = 'edge'

const apiConfig = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
})

const openai = new OpenAIApi(apiConfig)

export async function POST(req) {
  // Extrahiere die `messages` aus dem Body der Anfrage
  const { messages } = await req.json()

  // Fordere die OpenAI-API für die Antwort basierend auf dem Prompt an
  const response = await openai.createChatCompletion({
    model: 'gpt-3.5-turbo',
    stream: true,
    messages: messages,
    max_tokens: 500,
    temperature: 0.7,
    top_p: 1,
    frequency_penalty: 1,
    presence_penalty: 1,
  })

  // Konvertiere die Antwort in einen nutzerfreundlichen Text-Stream
  const stream = OpenAIStream(response)

  // Antworte mit dem Stream
  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 nutzen.

// 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 die Daten in dem gewünschten Format (z.B. number) zu erhalten.

CORS

Sie können CORS-Header für eine Response mit den standardmäßigen Web-API-Methoden setzen:

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 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',
    },
  })
}

Edge- und Node.js-Runtimes

Route Handler verfügen über eine isomorphe Web-API, um sowohl Edge- als auch Node.js-Runtimes nahtlos zu unterstützen, inklusive Streaming. Da Route Handler die gleiche Route Segment Configuration wie Pages und Layouts verwenden, unterstützen sie lang erwartete Funktionen wie allgemein nutzbare statisch regenerierte Route Handler.

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

export const runtime = 'edge' // 'nodejs' ist der Standardwert

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 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>`)
}
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>`)
}

Segmentkonfigurationsoptionen

Route Handler verwenden die gleiche Route Segment Configuration wie Pages 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.