Bahasa Indonesia

Jelajahi teknik modifikasi permintaan tingkat lanjut menggunakan middleware Next.js. Pelajari penanganan perutean kompleks, autentikasi, A/B testing, dan strategi lokalisasi untuk aplikasi web yang tangguh.

Kasus-Kasus Edge Middleware Next.js: Menguasai Pola Modifikasi Permintaan

Middleware Next.js menyediakan mekanisme yang kuat untuk mencegat dan memodifikasi permintaan sebelum mencapai rute aplikasi Anda. Kemampuan ini membuka berbagai kemungkinan, mulai dari pemeriksaan autentikasi sederhana hingga skenario A/B testing yang kompleks dan strategi internasionalisasi. Namun, memanfaatkan middleware secara efektif memerlukan pemahaman mendalam tentang kasus-kasus edge dan potensi kendalanya. Panduan komprehensif ini menjelajahi pola modifikasi permintaan tingkat lanjut, memberikan contoh praktis dan wawasan yang dapat ditindaklanjuti untuk membantu Anda membangun aplikasi Next.js yang tangguh dan beperforma tinggi.

Memahami Dasar-Dasar Middleware Next.js

Sebelum mendalami pola-pola tingkat lanjut, mari kita ulangi kembali dasar-dasar middleware Next.js. Fungsi middleware dieksekusi sebelum sebuah permintaan selesai, memungkinkan Anda untuk:

Fungsi middleware berada di dalam file middleware.js atau middleware.ts di direktori /pages atau /app Anda (tergantung pada versi dan pengaturan Next.js Anda). Fungsi tersebut menerima objek NextRequest yang merepresentasikan permintaan masuk dan dapat mengembalikan objek NextResponse untuk mengontrol perilaku selanjutnya.

Contoh: Middleware Autentikasi Dasar

Contoh ini mendemonstrasikan pemeriksaan autentikasi sederhana. Jika pengguna tidak diautentikasi (misalnya, tidak ada token yang valid dalam cookie), mereka akan diarahkan ke halaman login.


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const authToken = request.cookies.get('authToken')

  if (!authToken) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/protected/:path*'],
}

Middleware ini hanya akan berjalan untuk rute yang cocok dengan /protected/:path*. Middleware ini memeriksa keberadaan cookie authToken. Jika cookie tidak ada, pengguna diarahkan ke halaman /login. Jika ada, permintaan diizinkan untuk melanjutkan secara normal menggunakan NextResponse.next().

Pola Modifikasi Permintaan Tingkat Lanjut

Sekarang, mari kita jelajahi beberapa pola modifikasi permintaan tingkat lanjut yang menunjukkan kekuatan sebenarnya dari middleware Next.js.

1. A/B Testing dengan Cookie

A/B testing adalah teknik krusial untuk mengoptimalkan pengalaman pengguna. Middleware dapat digunakan untuk secara acak menugaskan pengguna ke variasi aplikasi Anda yang berbeda dan melacak perilaku mereka. Pola ini mengandalkan cookie untuk mempertahankan varian yang ditugaskan kepada pengguna.

Contoh: A/B Testing Halaman Arahan


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const VARIANT_A = 'variantA'
const VARIANT_B = 'variantB'

export function middleware(request: NextRequest) {
  let variant = request.cookies.get('variant')?.value

  if (!variant) {
    // Randomly assign a variant
    variant = Math.random() < 0.5 ? VARIANT_A : VARIANT_B
    const response = NextResponse.next()
    response.cookies.set('variant', variant)
    return response
  }

  if (variant === VARIANT_A) {
    return NextResponse.rewrite(new URL('/variant-a', request.url))
  } else if (variant === VARIANT_B) {
    return NextResponse.rewrite(new URL('/variant-b', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/'],
}

Dalam contoh ini, ketika pengguna mengunjungi path root (/) untuk pertama kalinya, middleware secara acak menugaskan mereka ke variantA atau variantB. Varian ini disimpan dalam cookie. Permintaan berikutnya dari pengguna yang sama akan ditulis ulang ke /variant-a atau /variant-b, tergantung pada varian yang ditugaskan kepada mereka. Ini memungkinkan Anda untuk menyajikan halaman arahan yang berbeda dan melacak mana yang berkinerja lebih baik. Pastikan Anda memiliki rute yang ditentukan untuk /variant-a dan /variant-b di aplikasi Next.js Anda.

Pertimbangan Global: Saat melakukan A/B testing, pertimbangkan variasi regional. Desain yang cocok di Amerika Utara mungkin tidak seefektif di Asia. Anda bisa menggunakan data geolokasi (diperoleh melalui pencarian alamat IP atau preferensi pengguna) untuk menyesuaikan A/B test dengan wilayah tertentu.

2. Lokalisasi (i18n) dengan Penulisan Ulang URL

Internasionalisasi (i18n) sangat penting untuk menjangkau audiens global. Middleware dapat digunakan untuk secara otomatis mendeteksi bahasa pilihan pengguna dan mengarahkan mereka ke versi situs Anda yang dilokalkan.

Contoh: Mengalihkan berdasarkan Header `Accept-Language`


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const SUPPORTED_LANGUAGES = ['en', 'fr', 'es', 'de']
const DEFAULT_LANGUAGE = 'en'

function getPreferredLanguage(request: NextRequest): string {
  const acceptLanguage = request.headers.get('accept-language')
  if (!acceptLanguage) {
    return DEFAULT_LANGUAGE
  }

  const languages = acceptLanguage.split(',').map((lang) => lang.split(';')[0].trim())

  for (const lang of languages) {
    if (SUPPORTED_LANGUAGES.includes(lang)) {
      return lang
    }
  }

  return DEFAULT_LANGUAGE
}

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname

  // Check if there's an existing locale in the pathname
  if (
    SUPPORTED_LANGUAGES.some(
      (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
    )
  ) {
    return NextResponse.next()
  }

  const preferredLanguage = getPreferredLanguage(request)

  return NextResponse.redirect(
    new URL(`/${preferredLanguage}${pathname}`, request.url)
  )
}

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico).*)'
  ],
}

Middleware ini mengekstrak header Accept-Language dari permintaan dan menentukan bahasa pilihan pengguna. Jika URL belum berisi awalan bahasa (misalnya, /en/about), middleware akan mengarahkan pengguna ke URL yang dilokalkan yang sesuai (misalnya, /fr/about untuk bahasa Prancis). Pastikan Anda memiliki struktur folder yang sesuai di direktori `/pages` atau `/app` Anda untuk lokal yang berbeda. Sebagai contoh, Anda akan memerlukan file `/pages/en/about.js` dan `/pages/fr/about.js`.

Pertimbangan Global: Pastikan implementasi i18n Anda menangani bahasa kanan-ke-kiri (misalnya, Arab, Ibrani) dengan benar. Selain itu, pertimbangkan untuk menggunakan Content Delivery Network (CDN) untuk menyajikan aset yang dilokalkan dari server yang lebih dekat dengan pengguna Anda, sehingga meningkatkan performa.

3. Feature Flags

Feature flags memungkinkan Anda untuk mengaktifkan atau menonaktifkan fitur di aplikasi Anda tanpa menyebarkan kode baru. Ini sangat berguna untuk meluncurkan fitur baru secara bertahap atau untuk menguji fitur di lingkungan produksi. Middleware dapat digunakan untuk memeriksa status feature flag dan memodifikasi permintaan sesuai dengan itu.

Contoh: Mengaktifkan Fitur Beta


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const BETA_FEATURE_ENABLED = process.env.BETA_FEATURE_ENABLED === 'true'

export function middleware(request: NextRequest) {
  if (BETA_FEATURE_ENABLED && request.nextUrl.pathname.startsWith('/new-feature')) {
    return NextResponse.next()
  }

  // Optionally redirect to a "feature unavailable" page
  return NextResponse.rewrite(new URL('/feature-unavailable', request.url))
}

export const config = {
  matcher: ['/new-feature/:path*'],
}

Middleware ini memeriksa nilai variabel lingkungan BETA_FEATURE_ENABLED. Jika diatur ke true dan pengguna mencoba mengakses rute di bawah /new-feature, permintaan diizinkan untuk melanjutkan. Jika tidak, pengguna dialihkan ke halaman /feature-unavailable. Ingatlah untuk mengonfigurasi variabel lingkungan dengan tepat untuk lingkungan yang berbeda (pengembangan, staging, produksi).

Pertimbangan Global: Saat menggunakan feature flags, pertimbangkan implikasi hukum dari mengaktifkan fitur yang mungkin tidak sesuai dengan peraturan di semua wilayah. Misalnya, fitur yang terkait dengan privasi data mungkin perlu dinonaktifkan di negara-negara tertentu.

4. Deteksi Perangkat dan Perutean Adaptif

Aplikasi web modern harus responsif dan beradaptasi dengan berbagai ukuran layar dan kemampuan perangkat. Middleware dapat digunakan untuk mendeteksi jenis perangkat pengguna dan mengarahkan mereka ke versi situs Anda yang dioptimalkan.

Contoh: Mengalihkan Pengguna Seluler ke Subdomain yang Dioptimalkan untuk Seluler


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { device } from 'detection'

export function middleware(request: NextRequest) {
  const userAgent = request.headers.get('user-agent')

  if (userAgent) {
    const deviceType = device(userAgent)

    if (deviceType.type === 'phone') {
      const mobileUrl = new URL(request.url)
      mobileUrl.hostname = 'm.example.com'
      return NextResponse.redirect(mobileUrl)
    }
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/'],
}

Contoh ini menggunakan pustaka `detection` untuk menentukan jenis perangkat pengguna berdasarkan header User-Agent. Jika pengguna menggunakan ponsel, mereka akan diarahkan ke subdomain m.example.com (dengan asumsi Anda memiliki versi situs yang dioptimalkan untuk seluler yang dihosting di sana). Ingatlah untuk menginstal paket `detection`: `npm install detection`.

Pertimbangan Global: Pastikan logika deteksi perangkat Anda memperhitungkan variasi regional dalam penggunaan perangkat. Misalnya, feature phone masih banyak digunakan di beberapa negara berkembang. Pertimbangkan untuk menggunakan kombinasi deteksi User-Agent dan teknik desain responsif untuk solusi yang lebih tangguh.

5. Pengayaan Header Permintaan

Middleware dapat menambahkan informasi ke header permintaan sebelum diproses oleh rute aplikasi Anda. Ini berguna untuk menambahkan metadata khusus, seperti peran pengguna, status autentikasi, atau ID permintaan, yang dapat digunakan oleh logika aplikasi Anda.

Contoh: Menambahkan ID Permintaan


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'

export function middleware(request: NextRequest) {
  const requestId = uuidv4()
  const response = NextResponse.next()
  response.headers.set('x-request-id', requestId)
  return response
}

export const config = {
  matcher: ['/api/:path*'], // Only apply to API routes
}

Middleware ini menghasilkan ID permintaan unik menggunakan pustaka uuid dan menambahkannya ke header x-request-id. ID ini kemudian dapat digunakan untuk tujuan logging, tracing, dan debugging. Ingatlah untuk menginstal paket `uuid`: `npm install uuid`.

Pertimbangan Global: Saat menambahkan header khusus, perhatikan batas ukuran header. Melebihi batas ini dapat menyebabkan kesalahan yang tidak terduga. Selain itu, pastikan informasi sensitif apa pun yang ditambahkan ke header dilindungi dengan benar, terutama jika aplikasi Anda berada di belakang reverse proxy atau CDN.

6. Peningkatan Keamanan: Rate Limiting

Middleware dapat bertindak sebagai garis pertahanan pertama terhadap serangan berbahaya dengan menerapkan rate limiting. Ini mencegah penyalahgunaan dengan membatasi jumlah permintaan yang dapat dibuat oleh klien dalam rentang waktu tertentu.

Contoh: Rate Limiting Dasar menggunakan Penyimpanan Sederhana


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const requestCounts: { [ip: string]: number } = {}
const WINDOW_SIZE_MS = 60000; // 1 minute
const MAX_REQUESTS_PER_WINDOW = 100;

export function middleware(request: NextRequest) {
  const clientIP = request.ip || '127.0.0.1' // Get client IP, default to localhost for local testing

  if (!requestCounts[clientIP]) {
    requestCounts[clientIP] = 0;
  }

  requestCounts[clientIP]++;

  if (requestCounts[clientIP] > MAX_REQUESTS_PER_WINDOW) {
    return new NextResponse(
      JSON.stringify({ message: 'Too many requests' }),
      { status: 429, headers: { 'Content-Type': 'application/json' } }
    );
  }

  // Reset count after window
  setTimeout(() => {
    requestCounts[clientIP]--;
    if (requestCounts[clientIP] <= 0) {
        delete requestCounts[clientIP];
    }
  }, WINDOW_SIZE_MS);

  return NextResponse.next();
}

export const config = {
  matcher: ['/api/:path*'], // Apply to all API routes
}

Contoh ini memelihara penyimpanan dalam memori sederhana (requestCounts) untuk melacak jumlah permintaan dari setiap alamat IP. Jika klien melebihi MAX_REQUESTS_PER_WINDOW dalam WINDOW_SIZE_MS, middleware akan mengembalikan kesalahan 429 Too Many Requests. Penting: Ini adalah contoh yang disederhanakan dan tidak cocok untuk lingkungan produksi karena tidak dapat diskalakan dan rentan terhadap serangan denial-of-service. Untuk penggunaan produksi, pertimbangkan untuk menggunakan solusi rate-limiting yang lebih tangguh seperti Redis atau layanan rate-limiting khusus.

Pertimbangan Global: Strategi rate-limiting harus disesuaikan dengan karakteristik spesifik aplikasi Anda dan distribusi geografis pengguna Anda. Pertimbangkan untuk menggunakan batas tarif yang berbeda untuk wilayah atau segmen pengguna yang berbeda.

Kasus Edge dan Potensi Kendala

Meskipun middleware adalah alat yang kuat, penting untuk menyadari batasan dan potensi kendalanya:

Praktik Terbaik Menggunakan Middleware Next.js

Untuk memaksimalkan manfaat middleware Next.js dan menghindari potensi masalah, ikuti praktik terbaik ini:

Kesimpulan

Middleware Next.js menawarkan cara yang ampuh untuk memodifikasi permintaan dan menyesuaikan perilaku aplikasi Anda di edge. Dengan memahami pola modifikasi permintaan tingkat lanjut yang dibahas dalam panduan ini, Anda dapat membangun aplikasi Next.js yang tangguh, beperforma tinggi, dan sadar global. Ingatlah untuk mempertimbangkan dengan cermat kasus-kasus edge dan potensi kendala, serta ikuti praktik terbaik yang diuraikan di atas untuk memastikan bahwa fungsi middleware Anda andal dan dapat dipelihara. Manfaatkan kekuatan middleware untuk menciptakan pengalaman pengguna yang luar biasa dan membuka kemungkinan baru untuk aplikasi web Anda.