Episode ini membahas pengamanan route handler dan validasi request dengan Zod, CORS dan CSRF dengan secure headers, penanganan data sensitif dan secrets, serta rate limiting dan pencegahan penyalahgunaan API.

API adalah permukaan serangan utama sebuah aplikasi. Endpoint yang menerima input dari publik — tanpa validasi, tanpa batasan, tanpa keamanan header — adalah undangan terbuka bagi penyalahgunaan.
Episode 13 membahas pengamanan route handler dan validasi request dengan Zod, konfigurasi CORS dan proteksi CSRF dengan secure headers, penanganan data sensitif dan secrets, serta rate limiting dan pencegahan penyalahgunaan.
Aturan nomor satu: jangan pernah mempercayai input. Setiap body, query, dan header dari request harus divalidasi sebelum dipakai. Kombinasikan Zod dengan route handler:
import { NextResponse } from "next/server"
import { z } from "zod"
const schema = z.object({
email: z.string().email(),
nama: z.string().min(3).max(100),
})
export async function POST(request) {
const body = await request.json()
const parsed = schema.safeParse(body)
if (!parsed.success) {
return NextResponse.json(
{ error: "Data tidak valid" },
{ status: 400 }
)
}
const data = parsed.data
return NextResponse.json({ ok: true, data }, { status: 201 })
}schema.safeParse(body) memvalidasi seluruh body. Mengembalikan status 400 saat validasi gagal mencegah data kotor masuk ke logika aplikasi.
Terapkan batas pada input: panjang string, jumlah item dalam array, dan nilai maksimum untuk angka. Validasi server juga harus menyaring input berbahaya seperti SQL injection — selalu gunakan prepared statement atau ORM saat berinteraksi dengan database, jangan menyusun query dari string langsung.
CORS (Cross-Origin Resource Sharing) menentukan domain mana yang boleh memanggil API kalian. Di route handler, atur header secara eksplisit:
export async function GET() {
const headers = new Headers()
headers.set("Access-Control-Allow-Origin", "https://app.example.com")
headers.set("Access-Control-Allow-Methods", "GET, POST")
return NextResponse.json({ data: [] }, { headers })
}Header Access-Control-Allow-Origin di atas membatasi pemanggilan ke domain app.example.com. Jangan memakai wildcard untuk API yang membawa data pengguna.
CSRF menyerang aplikasi yang mempercayai cookie secara implisit. Server Actions Next.js dilindungi bawaan, tapi untuk route handler yang memakai cookie sesi, pastikan ada token CSRF. Lengkapi juga dengan secure headers:
X-Frame-Options: DENY mencegah clickjacking.X-Content-Type-Options: nosniff mencegah sniffing tipe konten.Strict-Transport-Security memaksa koneksi HTTPS.Content-Security-Policy membatasi sumber script dan style.Header di atas bisa ditetapkan otomatis lewat opsi headers di next.config.mjs, sehingga berlaku untuk seluruh response.
Secrets seperti kunci API, token database, dan kunci signing hanya boleh eksis di environment server. Jangan pernah: menaruhnya di kode, menaruhnya di variabel NEXT_PUBLIC_, atau menaruhnya di riwayat Git. Gunakan file .env.local (yang masuk .gitignore) dan inject nilai asli dari platform deployment di production.
Data sensitif seperti password wajib di-hash — jangan pernah menyimpan password plaintext. Gunakan library seperti bcrypt dengan salt. Prinsip data minimization: simpan hanya data yang benar-benar dibutuhkan, dan hapus atau anonimkan data lama. Semakin sedikit data sensitif yang tersimpan, semakin kecil dampaknya saat terjadi kebocoran.
API publik wajib dibatasi frekuensinya untuk mencegah brute force dan spam. Di edge, gunakan penyimpanan terdistribusi seperti Upstash Redis:
import { Ratelimit } from "@upstash/ratelimit"
import { Redis } from "@upstash/redis"
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "1 m"),
})
export async function POST(request) {
const ip = request.headers.get("x-forwarded-for")
const result = await ratelimit.limit(`api-${ip}`)
if (!result.success) {
return new Response("Terlalu banyak permintaan", { status: 429 })
}
return new Response("OK")
}ratelimit.limit("api-" + ip) mengembalikan success yang menandakan apakah request melebihi kuota 10 request per menit. Status 429 menandakan Too Many Requests.
Inti yang harus dibawa pulang:
Di episode 14 selanjutnya kita akan membahas networking performance dan caching — strategi caching dengan Cache-Control dan ISR, edge caching dan integrasi CDN, optimasi pengambilan data untuk mengurangi latensi API, serta prefetching dan resource scheduling. Aplikasi kalian akan terasa jauh lebih cepat.