kodingup.camp

Beranda/Next.js Mastery: Full-Stack, Auth & Deploy

Proxy (eks-Middleware)

Modul 5 dari 115 menit bacaProxy

Isi modul

    Kenapa ini penting

    Beberapa keputusan sebaiknya diambil sebelum halaman mulai dirender: pengunjung yang belum login dan mencoba membuka /admin sebaiknya langsung diarahkan ke /login; pengunjung dari URL lama /artikel/... sebaiknya dialihkan ke /blog/...; atau setiap response perlu ditambah header keamanan. Kalau logika seperti ini ditaruh di setiap halaman, kodenya berulang di mana-mana dan mudah terlewat.

    Next.js menyediakan satu "pos jaga" di depan seluruh aplikasi: Proxy. Kalau kamu pernah membaca tutorial Next.js lama, fitur ini dulu bernama Middleware dengan file middleware.js. Mulai Next.js 16, Middleware diganti nama menjadi Proxy dengan file proxy.js. Fungsinya sama, namanya diubah supaya tidak tertukar dengan konsep middleware di Express dan supaya jelas perannya: lapisan jaringan yang berdiri di depan aplikasi. File middleware.js masih dikenali tapi sudah deprecated (tidak dianjurkan, akan dihapus di masa depan).

    Analogi: Proxy adalah satpam di gerbang kompleks perumahan. Dia tidak masuk ke tiap rumah dan tidak memeriksa isi lemari. Dia hanya melihat sekilas: "Bawa kartu penghuni? Silakan. Tidak bawa? Ke pos tamu dulu." Pemeriksaan detail tetap dilakukan pemilik rumah (halaman dan Data Access Layer).

    Konsep inti

    Konvensi file

    Yang bisa dilakukan Proxy

    Aksi Kode Efek
    Lanjut normal NextResponse.next() Request diteruskan ke halaman
    Redirect NextResponse.redirect(new URL('/login', request.url)) Browser diminta pindah URL (URL di address bar berubah)
    Rewrite NextResponse.rewrite(new URL('/maintenance', request.url)) Konten diambil dari path lain, URL di address bar tetap
    Ubah header res.headers.set('x-nama', 'nilai') pada hasil NextResponse.next() Menambah header response
    Jawab langsung Response.json({...}, { status: 403 }) Request berhenti di Proxy

    Proxy juga bisa membaca cookie (request.cookies.get('session')?.value), header, dan path (request.nextUrl.pathname).

    matcher: kapan Proxy dijalankan

    Tanpa konfigurasi, Proxy berjalan di setiap request, termasuk file statis _next/static, gambar _next/image, dan isi folder public/. Itu boros, dan berbahaya: logika redirect bisa tak sengaja memblokir CSS dan gambar sehingga halaman login tampil berantakan. Karena itu export config.matcher:

    export const config = {
      matcher: ['/admin/:path*', '/login'], // hanya path ini
    }
    

    Runtime dan batasan

    Di Next.js 16, Proxy berjalan di runtime Node.js secara default, jadi kamu bisa memakai library Node seperti jose. Opsi runtime tidak tersedia di file proxy; menuliskannya justru menyebabkan error. Proxy bukan tempat untuk pengambilan data yang lambat: opsi cache pada fetch tidak berlaku di sini, dan setiap milidetik di Proxy menambah waktu tunggu semua halaman yang dicocokkan.

    Proxy bukan pengaman utama

    Ini pesan terpenting modul ini, dan dokumentasi resmi menekankannya: Proxy cocok untuk pengecekan optimistis (melihat apakah cookie session ada dan valid, lalu redirect), bukan solusi otorisasi lengkap. Alasannya:

    1. Server Action dipanggil lewat POST ke route tempat ia dipakai. Kalau matcher diubah atau action dipindah ke route lain, perlindungan Proxy bisa hilang diam-diam.
    2. Proxy idealnya tidak query database, jadi ia tidak tahu apakah user sudah diblokir atau perannya berubah.

    Jadi pola yang benar: Proxy untuk UX (redirect cepat sebelum render), ditambah cek ulang di Data Access Layer dan di setiap Server Action (Modul 6).

    Contoh kode

    Proxy di proyek akhir: redirect /admin ke /login bila belum login, dan sebaliknya.

    // proxy.js  (root proyek, sejajar dengan folder app/)
    import { NextResponse } from 'next/server'
    import { decrypt } from '@/lib/session'
    
    // Cek OPTIMISTIS: cuma baca cookie, tanpa query DB. Keamanan sebenarnya tetap di DAL.
    export async function proxy(request) {
      const { pathname } = request.nextUrl
      const session = await decrypt(request.cookies.get('session')?.value)
      const isAdmin = session?.role === 'admin'
    
      if (pathname.startsWith('/admin') && !isAdmin) {
        return NextResponse.redirect(new URL('/login', request.nextUrl))
      }
      if (pathname === '/login' && isAdmin) {
        return NextResponse.redirect(new URL('/admin', request.nextUrl))
      }
      return NextResponse.next()
    }
    
    export const config = {
      matcher: ['/admin/:path*', '/login'],
    }
    
    curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" http://localhost:3000/admin
    # → 307 http://localhost:3000/login     (tanpa cookie session)
    

    Saat build, Next.js menampilkan baris ƒ Proxy (Middleware) di ringkasan route, tanda Proxy-mu terdeteksi.

    Contoh kedua: kumpulan tugas umum dalam satu Proxy. Pengalihan URL lama, mode maintenance dengan rewrite, dan header tambahan.

    // proxy.js  (contoh lain, bukan untuk proyek akhir)
    import { NextResponse } from 'next/server'
    
    export function proxy(request) {
      const { pathname } = request.nextUrl
    
      // 1. URL lama /artikel/slug → /blog/slug  (redirect permanen 308)
      if (pathname.startsWith('/artikel/')) {
        const url = request.nextUrl.clone()
        url.pathname = pathname.replace('/artikel/', '/blog/')
        return NextResponse.redirect(url, 308)
      }
    
      // 2. Mode maintenance: tampilkan isi /maintenance, URL di browser tetap
      if (process.env.MAINTENANCE === '1' && pathname !== '/maintenance') {
        return NextResponse.rewrite(new URL('/maintenance', request.url))
      }
    
      // 3. Lanjut normal + header tambahan
      const response = NextResponse.next()
      response.headers.set('x-dibuat-dengan', 'Next.js 16')
      return response
    }
    
    export const config = {
      matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'],
    }
    

    Kalau proyekmu masih punya middleware.js dari versi lama, Next.js menyediakan codemod resmi yang mengganti nama file dan function-nya:

    npx @next/codemod@canary middleware-to-proxy .
    # middleware.js → proxy.js,  export function middleware() → export function proxy()
    

    Untuk redirect yang statis (tidak bergantung cookie atau logika), dokumentasi menganjurkan redirects() di next.config.mjs dulu, karena lebih sederhana dan tidak menjalankan kode di setiap request:

    // next.config.mjs  (potongan)
    const nextConfig = {
      async redirects() {
        return [{ source: '/artikel/:slug', destination: '/blog/:slug', permanent: true }]
      },
    }
    

    Kesalahan umum

    1. Masih menulis middleware.js di proyek Next.js 16. Masih jalan, tapi deprecated. Pakai proxy.js dan function proxy, atau jalankan codemod middleware-to-proxy.
    2. Tidak memasang matcher. Proxy jadi berjalan untuk CSS, JS, dan gambar; redirect auth bisa membuat halaman login tanpa style. Batasi dengan matcher atau negative lookahead.
    3. Menganggap Proxy cukup untuk keamanan. Kalau action dipindah atau matcher diubah, perlindungan hilang. Selalu cek ulang di DAL dan di setiap Server Action.
    4. Query database atau API lambat di Proxy. Setiap request yang cocok jadi menunggu. Cukup verifikasi token di cookie (cepat, tanpa jaringan).
    5. Menaruh proxy.js di dalam folder app/. File harus di root proyek (atau di src/ bila memakai folder src), sejajar dengan app/.

    Latihan

    1. Mudah, Buat Proxy yang menambahkan header x-waktu-request berisi waktu ISO ke semua halaman, kecuali file statis.
    2. Sedang, Buat Proxy yang membaca cookie lang. Kalau pengunjung membuka / dan cookie lang bernilai en, rewrite ke /en (URL di browser tetap /).
    3. Menantang, Buat Proxy yang memblokir path /api/internal/* dengan response JSON 403 kecuali request membawa header x-api-key yang cocok dengan process.env.INTERNAL_API_KEY.
    Lihat solusi
    // 1. proxy.js
    import { NextResponse } from 'next/server'
    
    export function proxy() {
      const res = NextResponse.next()
      res.headers.set('x-waktu-request', new Date().toISOString())
      return res
    }
    
    export const config = {
      matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
    }
    // curl -I localhost:3000 → x-waktu-request: 2026-10-24T03:15:00.000Z
    
    // 2. proxy.js
    import { NextResponse } from 'next/server'
    
    export function proxy(request) {
      if (request.cookies.get('lang')?.value === 'en') {
        return NextResponse.rewrite(new URL('/en', request.url))
      }
      return NextResponse.next()
    }
    
    export const config = { matcher: '/' }
    
    // 3. proxy.js
    import { NextResponse } from 'next/server'
    
    export function proxy(request) {
      const key = request.headers.get('x-api-key')
      if (!key || key !== process.env.INTERNAL_API_KEY) {
        return Response.json({ error: 'Forbidden' }, { status: 403 })
      }
      return NextResponse.next()
    }
    
    export const config = { matcher: '/api/internal/:path*' }
    // curl localhost:3000/api/internal/stats → 403 {"error":"Forbidden"}
    

    Ringkasan

    Video pendamping

    Kalau penjelasan di atas belum masuk, tonton ini. Video dari kanal YouTube pihak lain.

    Next.js 16 Middleware DEPRECATED - Authentication In Proxy Or Data Access Layer?ByteGrad · Bahasa InggrisBuka di YouTube

    Kuis pemahaman

    Pilih satu jawaban per soal. Penjelasan muncul setelah kamu menjawab.