# Cara Membuat API di Next.js dengan Route Handlers (route.ts)

> Panduan pemula cara membuat API di Next.js: route.ts, GET dan POST, validasi input, status code, error JSON konsisten, fetch dari client, dan form kontak.

URL: https://www.matthewswong.com/id/blog/cara-membuat-api-nextjs-route-handlers/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-10-17
- Updated: 2026-10-17
- Category: Web Dev

> **Key takeaway:** Cara membuat API di Next.js: buat file route.ts di folder app, misalnya app/api/hello/route.ts, lalu ekspor fungsi bernama sama dengan method HTTP, seperti GET atau POST. Parse body di dalam try dan catch, validasi tiap field, balas dengan status code yang tepat, dan simpan API key di environment variable tanpa awalan NEXT_PUBLIC.

Kamu sudah punya form kontak yang cantik, lalu tiba saatnya tombol Kirim benar-benar melakukan sesuatu. Kamu butuh sebuah alamat di server yang menerima data, memeriksanya, dan mengirim email. Di tahap ini banyak pemula membuat file di tempat yang salah, mendapat 405 dan 404 yang membingungkan, atau menaruh API key di kode yang ikut terkirim ke browser.

Panduan ini membangun satu API kecil dari nol dengan Route Handlers di Next.js 15 (App Router): GET, POST, validasi, error yang konsisten, pemanggilan dari client, dan endpoint form kontak. Setiap pernyataan tentang Next.js dan fetch sudah dicocokkan dengan dokumentasi resmi yang tercantum di bagian sumber, dan polanya sama dengan route kontak di situs ini.

## Mau konsepnya dulu, dengan simulasi interaktif?

Panduan ini fokus pada langkah dan error. Lesson gratis kami membahas rate limit, versioning API, dan alur satu request lewat route yang aman, lengkap dengan kuis:

Belajar API Route Next.js: Validasi, Rate Limit, Versioning

Kerjakan panduan ini dulu supaya kamu punya route yang berjalan, lalu lanjut ke lesson untuk membuatnya tahan banting.

Output yang diharapkan

## Yang kamu butuhkan

Semua ini bisa dikerjakan di laptop, tanpa server dan tanpa biaya tambahan.

Proyek Next.js 15 dengan App Router (folder app), misalnya hasil create-next-app. Panduan ini memakai TypeScript.

Dev server yang berjalan dengan npm run dev, biasanya di http://localhost:3000.

curl di terminal untuk mencoba API tanpa harus membuat form dulu.

Satu file .env.local di root proyek untuk langkah terakhir.

## Langkah 1: Buat route.ts pertama dan panggil dengan GET

Route Handler adalah file route.ts atau route.js di dalam folder app. Di dalamnya kamu mengekspor satu fungsi untuk tiap method HTTP yang ingin dilayani: GET, POST, PUT, PATCH, DELETE, HEAD, atau OPTIONS. Nama fungsinya harus huruf besar persis seperti itu. Response.json membuat body JSON sekaligus memasang header Content-Type menjadi application/json.

curl menampilkan baris status HTTP 200, header content-type application/json, dan body berisi message dengan teks Halo dari route handler.

Folder yang sama tidak boleh punya route.ts dan page.tsx sekaligus. Dokumentasi Next.js menyebut app/page.js dan app/route.js berbenturan, sedangkan app/api/route.js aman. Karena itu API hampir selalu ditaruh di subfolder seperti api.

## Langkah 2: Tambahkan POST dan baca body JSON

Body request dibaca dengan method standar Web API, yaitu request.json(). Method ini melempar error kalau body kosong atau bukan JSON yang valid, jadi bungkus di try dan catch. Kalau tidak, kesalahan milik klien berubah menjadi 500 yang seolah-olah salah servermu. Balas 400 dengan code yang bisa dibaca mesin.

Dengan body JSON yang benar kamu mendapat status 200 dan success bernilai true. Dengan body bukan JSON kamu mendapat status 400 dan code invalid_json.

Di Pages Router dulu ada pengaturan bodyParser. Di Route Handlers kamu tidak memerlukannya. Cukup panggil request.json(), atau request.formData() kalau form-mu mengirim multipart.

## Langkah 3: Validasi input sebelum dipakai

Anggap body sebagai teks yang tidak tepercaya. Ubah dulu setiap field ke bentuk yang aman, yaitu bukan string menjadi string kosong, lalu di-trim dan dipotong sampai panjang maksimum. Baru sesudah itu periksa aturannya. Urutan ini mencegah TypeError saat seseorang mengirim angka atau objek di field yang kamu kira teks.

Setiap penolakan sebaiknya menyebut field mana yang salah dan rentang yang benar. Pemanggil bisa memperbaiki request tanpa membaca source-mu. Route kontak di situs ini memakai pola yang sama: clamp dulu, periksa kemudian.

Nama satu huruf mendapat 400 invalid_request dengan hint yang menyebut name. Email tanpa tanda at mendapat 400 dengan hint yang menyebut email.

## Langkah 4: Pilih status code dan satukan bentuk error

Status code menyatakan kategori: 400 untuk request yang salah, 405 untuk method yang tidak didukung, 429 untuk terlalu banyak request, 500 untuk salah konfigurasi di server, dan 502 kalau layanan lain yang kamu panggil gagal. Satu helper kecil yang membangun semua error menjaga bentuknya sama, jadi client cukup membaca code, bukan menebak dari kalimat.

Perhatikan argumen kedua Response.json. Itu objek opsi yang berisi status, statusText, dan headers, bukan angka. Jadi status ditulis sebagai properti status di dalam objek itu. Cache-Control no-store pada error mencegah jawaban gagal disimpan.

## Langkah 5: Baca query string dan dynamic segment

Query string dibaca lewat request.nextUrl.searchParams. Untuk URL seperti /api/items/42, buat folder bernama dengan kurung siku, app/api/items/[id]/route.ts. Mulai Next.js 15, params yang diterima fungsi adalah Promise, jadi harus di-await sebelum dipakai.

Memanggil /api/search?query=hello mengembalikan query bernilai hello. Memanggil /api/items/42 mengembalikan id bernilai 42.

Kalau kamu menyalin contoh lama yang menulis params.id tanpa await, kode itu mengira params sudah berupa objek biasa. Di Next.js 15 itu sudah tidak benar. Tipe Promise dan await di atas adalah bentuk yang tercantum di dokumentasi sekarang.

## Langkah 6: Panggil API dari client dengan fetch

Dari komponen client, kirim POST dengan header Content-Type application/json dan body hasil JSON.stringify. Jebakan terbesarnya ada di sini: fetch hanya reject kalau terjadi kegagalan level jaringan. Kalau server menjawab 400 atau 500, promise tetap berhasil dan kamu mendapat Response biasa. Periksa res.ok sendiri.

Karena error dari API-mu sudah satu bentuk, client cukup menampilkan err.hint kepada pengguna. Tidak perlu if berlapis untuk setiap jenis kegagalan.

## Langkah 7: Susun endpoint form kontak dengan rahasia di server

Sekarang gabungkan semuanya. API key penyedia email dibaca dari process.env di dalam route, tempat ia hanya ada di server. Periksa dulu apakah key ada, lalu parse, validasi, panggil penyedia email, dan cek hasilnya. Kalau penyedia gagal, catat detailnya di log server dan balas 502 dengan pesan umum.

Form yang hanya dipakai halaman Next.js milikmu sendiri kadang lebih pas memakai Server Actions. Perbandingannya ada di 

artikel Server Actions di production

. Route Handler tetap pilihan yang benar kalau API-mu dipanggil dari luar, misalnya aplikasi mobile atau webhook.

Tanpa EMAIL_API_KEY route menjawab 500 service_unavailable. Dengan key dan input valid, route menjawab success bernilai true. Nilai key tidak pernah muncul di body respons maupun di bundle JavaScript browser.

## Error umum dan cara memperbaikinya

Baca gejalanya, cocokkan dengan penyebab, lalu terapkan perbaikannya. Delapan kasus ini yang paling sering membuat pemula tersangkut.

Gejala

Penyebab

Perbaikan

POST mendapat 405 Method Not Allowed

File hanya mengekspor GET, atau nama fungsinya ditulis huruf kecil seperti post.

Ekspor export async function POST dengan huruf besar semua. Next.js menjawab 405 untuk method yang tidak diekspor.

404 saat membuka /api/hello

File tidak berada di app/api/hello/route.ts, atau namanya bukan route.ts.

Cocokkan path folder dengan URL dan pastikan nama file persis route.ts.

Dev server atau build mengeluh route dan page berbenturan

route.ts diletakkan satu folder dengan page.tsx.

Pindahkan route.ts ke subfolder, misalnya app/api/contact/route.ts.

500 saat body kosong atau bukan JSON

request.json() melempar error dan tidak ada try dan catch.

Bungkus parse di try dan catch, balas 400 dengan code invalid_json.

500 dengan TypeError saat memanggil trim

Field ternyata bukan string, misalnya angka atau objek.

Paksa dulu lewat clamp: bukan string menjadi string kosong, baru trim dan potong.

params.id undefined atau error di dynamic route

Di Next.js 15 params adalah Promise, tetapi dipakai seperti objek biasa.

Tipekan sebagai Promise dan tulis const id dari await params.

catch di client tidak jalan padahal server menjawab 400

fetch hanya reject pada kegagalan jaringan, bukan pada status error HTTP.

Cek res.ok setelah fetch, lalu baca body error dan lempar sendiri.

process.env.EMAIL_API_KEY bernilai undefined, atau key terlihat di browser

File .env.local salah tempat, atau key diberi awalan NEXT_PUBLIC sehingga nilainya disisipkan ke bundle browser saat build.

Taruh .env.local di root proyek, bukan di folder src. Pakai nama tanpa awalan, baca hanya di route, dan jangan commit file env.

## Checklist sebelum API kamu dipakai orang lain

Route ada di app/api/nama-endpoint/route.ts dan tidak sejajar dengan page.tsx.

Nama fungsi yang diekspor huruf besar dan sesuai method yang ingin dilayani.

request.json() dibungkus try dan catch, dan body rusak dijawab 400.

Setiap field di-clamp dan divalidasi, dan hint menyebut field serta rentangnya.

Semua error lewat satu helper dengan code yang stabil dan status yang tepat.

API key dibaca dari process.env tanpa awalan NEXT_PUBLIC, dan file .env tidak masuk git.

## Belajar lebih dalam

Route yang kamu buat di sini sengaja memakai pola yang sama dengan route kontak di situs ini: parse di try dan catch, field di-clamp, kegagalan penyedia email dijawab 502 dengan pesan umum, dan error dibangun satu helper. Yang belum kita bahas adalah membatasi seberapa sering satu klien boleh memanggil API, memberi versi pada path, dan memensiunkan path lama dengan header.

Lanjut ke lesson: Belajar API Route Next.js, Validasi, Rate Limit, Versioning

Lesson itu membedah kode nyata dari situs ini, termasuk satu bug rate limit yang pernah terjadi karena memercayai header yang bisa dipalsukan, dan ditutup dengan latihan yang bisa kamu jalankan di laptop.

## Sumber dan dokumentasi resmi

- Dokumentasi Next.js: Route Handlers
- Dokumentasi Next.js: referensi file route.js
- Dokumentasi Next.js: environment variables
- MDN: Using the Fetch API
- MDN: Response.json() static method

## Source links

- https://nextjs.org/docs/app/getting-started/route-handlers
- https://nextjs.org/docs/app/api-reference/file-conventions/route
- https://nextjs.org/docs/app/guides/environment-variables
- https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
- https://developer.mozilla.org/en-US/docs/Web/API/Response/json_static

## FAQ

### Apa itu Route Handler di Next.js dan apa bedanya dengan page?

Route Handler adalah file route.ts di folder app yang menjawab request HTTP memakai API Web Request dan Response. Page merender tampilan, sedangkan Route Handler mengembalikan data seperti JSON. Keduanya tidak boleh berada di folder yang sama, jadi API biasanya ditaruh di subfolder seperti api.

### Kenapa POST ke API Next.js saya error 405 Method Not Allowed?

Next.js hanya melayani method yang kamu ekspor dari route.ts, dan menjawab 405 untuk method lainnya. Pastikan kamu mengekspor fungsi bernama POST dengan huruf besar, bukan post. Pastikan juga request memang menuju route yang kamu edit.

### Bagaimana cara membaca body JSON dan query parameter di Route Handler?

Baca body dengan await request.json() dan bungkus di try dan catch karena ia melempar error kalau body kosong atau tidak valid. Baca query parameter dengan request.nextUrl.searchParams.get, misalnya untuk /api/search?query=hello. Untuk data form pakai request.formData().

### Kenapa params harus di-await di route Next.js 15?

Mulai Next.js 15, params di Route Handler adalah Promise. Kamu menipekannya sebagai Promise lalu menulis const id dari await params. Contoh lama yang langsung membaca params.id masih mengira perilaku yang lama.

### Apakah aman memakai API key di dalam route.ts?

Aman, selama kamu membacanya dari process.env tanpa awalan NEXT_PUBLIC. Variabel tanpa awalan hanya ada di server, sedangkan yang berawalan disisipkan ke JavaScript browser saat build. Jangan lupa jaga file env tetap di luar git.

---

- HTML version: https://www.matthewswong.com/id/blog/cara-membuat-api-nextjs-route-handlers/
- English: https://www.matthewswong.com/en/blog/cara-membuat-api-nextjs-route-handlers/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
