Nextjs Route Handlers API Endpoints Query Body Parsing Response Helpers
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama Next.js untuk melanjutkan topik lainnya.
Kembali ke rujukan utama Next.js untuk melanjutkan topik lainnya.
Route Handler di Next.js App Router adalah file route.ts atau route.js yang mengekspor function HTTP method seperti GET, POST, PUT, PATCH, DELETE, atau OPTIONS untuk melayani request di dalam folder app/.
Kalau page dipakai untuk merender UI, route handler dipakai untuk membentuk endpoint. Isinya bukan komponen React, melainkan logika request/response yang bekerja dengan Request, Response, atau NextResponse.
Topik yang dibahas di halaman ini:
URL, query string, headers, dan bodyResponse atau helper Next.jsRoute Handler dipakai saat kamu butuh endpoint HTTP yang eksplisit. Contoh paling umum:
GET yang perlu dibaca oleh browser, fetch, atau tool eksternalPakai route handler kalau yang kamu butuhkan adalah kontrak HTTP. Jangan pakai page kalau tujuan utamanya bukan UI.
Route handler bukan jawaban untuk semua kasus. Kadang opsi lain lebih pas:
Aturan praktisnya:
Route Handler hidup di folder app/ dan mengikuti pola route segment.
app/
api/
posts/
route.ts
posts/
[id]/
route.tsContoh hasil URL:
app/api/posts/route.ts → /api/postsapp/api/posts/[id]/route.ts → /api/posts/123Kalau folder berisi page.tsx dan route.ts, keduanya punya peran berbeda:
page.tsx menangani render UIroute.ts menangani request HTTP ke endpoint ituBiasanya satu path tidak dipakai untuk page dan endpoint sekaligus kalau bisa dihindari, supaya maksudnya jelas.
Di Route Handler, request datang sebagai objek Request standar Web API. Itu artinya kamu tidak membaca req.query atau req.body ala framework lama; kamu membaca input melalui API request modern.
Sumber data yang sering dipakai:
request.url → untuk mengakses URL penuhnew URL(request.url) → untuk parsing pathname dan query stringrequest.headers → untuk header seperti authorization, content-type, atau x-signatureawait request.json() → untuk body JSONawait request.text() → untuk payload text atau signature verificationawait request.formData() → untuk form submission atau multipart/form-dataQuery string dibaca dari URL.
export async function GET(request: Request) {
const url = new URL(request.url)
const page = Number(url.searchParams.get('page') ?? '1')
const q = url.searchParams.
Poin penting:
new URL(request.url) untuk parsing yang amansearchParams.get() mengembalikan string | nullParameter path dibaca dari argumen kedua handler.
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
return
Di App Router modern, params bisa berupa promise pada beberapa konteks. Karena itu aman untuk await params sebelum dipakai.
Kalau request mengirim JSON, body dibaca sekali lalu diparse.
export async function POST(request: Request) {
const body = await request.json()
const title = String(body.title ?? '')
return Response.json({ title })
}Hal yang perlu diingat:
request.json() hanya bisa dibaca sekalicontent-type kalau endpoint bisa menerima beberapa format bodyKalau request berasal dari form atau upload, gunakan formData().
export async function POST(request: Request) {
const formData = await request.formData()
const name = String(formData.get('name') ?? '')
return Response.json
Pola ini cocok untuk:
Route Handler bisa mengembalikan Response standar Web API atau helper dari Next.js seperti NextResponse.
Response.json()Untuk JSON API, ini biasanya pilihan paling simpel.
export async function GET() {
return Response.json({ ok: true })
}Kelebihannya:
new Response()Pakai ini kalau kamu butuh kontrol lebih langsung.
export async function GET() {
return new Response('hello', {
status: 200,
headers: {
'content-type': 'text/plain; charset=utf-8',
},
})
}Cocok untuk:
NextResponseNextResponse dipakai saat kamu butuh helper Next.js seperti cookies, redirect, atau manipulasi response yang lebih spesifik.
import { NextResponse } from 'next/server'
export function GET() {
return NextResponse.json({ ok: true })
}Atau untuk redirect:
import { NextResponse } from 'next/server'
export function GET() {
return NextResponse.redirect(new URL('/login', 'https://example.com'))
}Gunakan NextResponse kalau memang butuh fitur Next.js. Kalau cukup standar Web API, Response sering lebih bersih.
Inti Route Handler adalah alur yang sangat deterministik: request masuk, dibaca, divalidasi, diproses, lalu dibalas.
Kalau endpoint sudah dipahami lewat flow ini, debugging biasanya jadi lebih cepat. Yang perlu dicek tinggal di tahap mana request berhenti.
import { NextResponse } from 'next/server'
type Params = {
params: Promise<{ id: string }>
}
export
Hal yang ditunjukkan contoh ini:
GET dan POST bisa hidup dalam file yang samaURLSearchParamsparams dipakai untuk path dinamisPanduan cepatnya begini:
Response.json() → endpoint JSON biasanew Response() → text, stream, file, atau response customNextResponse.json() → JSON dengan utilitas Next.jsNextResponse.redirect() → redirect dari endpointNextResponse.rewrite() → rewrite path internal bila benar-benar perluKalau tim ingin endpoint yang terasa seperti API modern, Response.json() sering jadi default yang paling bersih.
Body request adalah stream. Setelah request.json() atau request.text() dipanggil, body itu tidak bisa dipakai ulang begitu saja.
Solusinya:
?page=2 tetap string sampai kamu ubah sendiri menjadi angka.
Kalau ini diabaikan:
content-typeEndpoint yang berharap JSON harus memastikan request memang mengirim JSON.
Kalau tidak:
Route Handler harus mengembalikan Response atau turunan yang valid. Mengembalikan object mentah bukan response HTTP.
Keduanya sama-sama berjalan di server, tapi tujuan dan kontraknya beda.
Kalau batas ini kabur, debugging auth, caching, dan shape request jadi lebih sulit.
Route Handler sebaiknya menjadi lapisan transport, bukan tempat semua aturan bisnis ditumpuk.
Pola yang lebih sehat:
Endpoint bisa saja dinamis, terutama kalau:
Jangan asumsi semua endpoint mendapat caching yang sama.
Gunakan kode status yang masuk akal:
400 untuk input tidak valid401 untuk belum autentikasi403 untuk tidak berhak404 untuk resource tidak ada422 untuk validasi semantik gagal500 untuk error server tak terdugaKalau semua error dibalas 500, diagnosa production jadi lebih susah.
Pisahkan tanggung jawab seperti ini supaya file route.ts tetap tipis dan endpoint lebih gampang diuji.