Tutorial Implementasi Middleware Next.js untuk Proteksi Dashboard & API Key
Pernahkah kamu merasa pusing saat harus mengecek status login pengguna di setiap halaman aplikasi webmu? Bayangkan jika kamu punya 50 halaman admin, dan kamu harus menulis kode if (!user) di setiap file tersebut. Rasanya sangat melelahkan dan rawan kesalahan, bukan? Di sinilah Middleware Next.js hadir sebagai pahlawan tanpa tanda jasa.
Dalam ekosistem Next.js modern, Middleware bukan sekadar fitur tambahan. Ia adalah "penjaga gerbang" yang berdiri tepat di depan aplikasi kamu, memeriksa setiap permintaan yang masuk sebelum sampai ke halaman atau API tujuan. Artikel ini akan mengupas tuntas cara mengamankan aplikasi kamu menggunakan teknik middleware yang efisien, aman, dan tentunya mudah kamu ikuti.
Apa Itu Middleware di Next.js?
Secara sederhana, middleware adalah potongan kode yang berjalan sebelum sebuah permintaan (request) selesai diproses. Karena berjalan di Edge Runtime, middleware sangat cepat dan efisien untuk menangani tugas-tugas seperti:
- Autentikasi: Memastikan pengguna sudah login sebelum mengakses dashboard.
- Otorisasi: Mengecek apakah pengguna punya hak akses (misalnya: admin vs member).
- Redirect: Mengarahkan pengguna dari halaman lama ke halaman baru atau dari
/ke/login. - Bot Detection: Memblokir permintaan mencurigakan yang berasal dari bot.
- Internationalization (i18n): Mendeteksi bahasa preferensi pengguna.
Dengan memindahkan logika keamanan ke level middleware, kode di dalam halaman (pages) atau API kamu menjadi lebih bersih dan fokus pada logika bisnis utama saja.
Persiapan Awal: Lokasi File Middleware
Untuk memulai, kamu hanya perlu membuat satu file bernama middleware.ts (atau .js) di root direktori proyek Next.js kamu. Jika kamu menggunakan folder src, letakkan file tersebut di dalam folder src.
// Struktur Folder
my-next-app/
├── src/
│ ├── middleware.ts
│ └── app/
├── package.json
└── ...
Ingat, Next.js hanya mengizinkan satu file middleware per proyek. Namun, jangan khawatir, kita bisa menangani banyak rute di dalam satu file tersebut dengan logika pengkondisian yang cerdas.
Langkah 1: Membuat Struktur Dasar Middleware
Mari kita mulai dengan kerangka dasar. Middleware menerima objek request dan dapat mengembalikan objek response.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Logika kamu akan ada di sini
return NextResponse.next();
}
Fungsi NextResponse.next() memberi tahu Next.js untuk "silakan lanjut" ke rute yang dituju. Jika kamu ingin memblokir atau mengalihkan, kamu akan menggunakan fungsi lain seperti NextResponse.redirect().
Langkah 2: Menggunakan Matcher untuk Efisiensi
Kamu tentu tidak ingin middleware berjalan di setiap file statis seperti gambar, CSS, atau favicon, kan? Itu akan memboroskan resource. Kamu bisa menggunakan config matcher untuk menentukan rute spesifik mana yang harus diproses oleh middleware.
export const config = {
matcher: [
'/dashboard/:path*', // Semua halaman di bawah /dashboard
'/api/admin/:path*', // Semua API di bawah /api/admin
'/profile', // Halaman profil spesifik
],
};
Dengan :path*, kamu memberi tahu Next.js untuk mengamankan halaman tersebut beserta semua sub-halamannya secara rekursif.
Langkah 3: Implementasi Proteksi Route (Halaman)
Sekarang, mari kita buat logika nyata. Katakanlah kamu menyimpan token akses pengguna di dalam HTTP-only Cookie bernama auth_token. Jika token ini tidak ada saat pengguna mencoba mengakses /dashboard, kita akan lempar mereka kembali ke halaman login.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/request';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth_token')?.value;
const isAuthPage = request.nextUrl.pathname.startsWith('/login');
const isDashboardPage = request.nextUrl.pathname.startsWith('/dashboard');
// 1. Jika mencoba akses dashboard tapi tidak ada token
if (isDashboardPage && !token) {
const loginUrl = new URL('/login', request.url);
// Tambahkan parameter callback agar setelah login bisa kembali ke sini
loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
// 2. Jika sudah punya token tapi mencoba akses halaman login lagi
if (isAuthPage && token) {
return NextResponse.redirect(new URL('/dashboard', request.url));
}
return NextResponse.next();
}
Tips Pro: Selalu gunakan new URL() saat melakukan redirect untuk memastikan path yang dihasilkan valid dan absolut.
Langkah 4: Mengamankan API Routes
Middleware tidak hanya bekerja untuk halaman HTML, tapi juga untuk Route Handlers (API). Ini sangat krusial jika kamu membangun aplikasi yang bersifat headless atau memiliki banyak interaksi AJAX.
Misalnya, untuk setiap permintaan ke /api/secure-data, kamu ingin memvalidasi Authorization header atau API Key.
if (request.nextUrl.pathname.startsWith('/api/admin')) {
const apiKey = request.headers.get('x-api-key');
if (apiKey !== process.env.ADMIN_API_KEY) {
return new NextResponse(
JSON.stringify({ success: false, message: 'Unauthorized access' }),
{ status: 401, headers: { 'content-type': 'application/json' } }
);
}
}
Dengan cara ini, kamu tidak perlu lagi melakukan pengecekan API key di dalam setiap file route.ts di folder API kamu. Cukup satu tempat di middleware!
Langkah 5: Role-Based Access Control (RBAC)
Terkadang, sekadar "sudah login" saja tidak cukup. Kamu mungkin punya peran pengguna seperti ADMIN, EDITOR, dan USER. Kamu bisa menyimpan informasi peran ini di dalam token JWT yang terenkripsi.
Bagaimana Cara Membaca JWT di Middleware?
Karena middleware berjalan di Edge Runtime, kamu tidak bisa menggunakan sembarang library Node.js. Gunakan library yang ringan seperti jose untuk melakukan verifikasi token.
import { jwtVerify } from 'jose';
async function verifyToken(token: string) {
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const { payload } = await jwtVerify(token, secret);
return payload;
}
export async function middleware(request: NextRequest) {
const token = request.cookies.get('auth_token')?.value;
if (request.nextUrl.pathname.startsWith('/admin')) {
if (!token) return NextResponse.redirect(new URL('/login', request.url));
try {
const payload = await verifyToken(token);
if (payload.role !== 'ADMIN') {
return NextResponse.rewrite(new URL('/403-forbidden', request.url));
}
} catch (err) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
return NextResponse.next();
}
Perhatikan penggunaan NextResponse.rewrite(). Berbeda dengan redirect, rewrite akan menampilkan konten halaman lain tanpa mengubah URL di browser pengguna. Ini sangat elegan untuk menampilkan halaman "Access Denied".
Optimasi Performa Middleware
Mengingat middleware berjalan di setiap request yang cocok dengan matcher, kamu harus menjaga kodenya tetap ringan. Berikut adalah beberapa tips optimasi yang bisa kamu terapkan:
- Hindari Database Query Langsung: Jangan melakukan query database (seperti Prisma atau Mongoose) di dalam middleware. Ini akan memperlambat waktu respon secara signifikan. Gunakan data yang ada di dalam JWT atau session cookie.
- Gunakan Cache Jika Memungkinkan: Jika kamu harus memanggil API eksternal untuk validasi, pertimbangkan untuk menggunakan mekanisme caching yang singkat.
- Minimalisir Library Eksternal: Gunakan Web Standard API (seperti
fetch,Headers,Response) sesering mungkin daripada mengimpor library besar.
Kesalahan Umum yang Sering Terjadi
Banyak developer terjebak dalam beberapa lubang saat mengimplementasikan middleware di Next.js. Pastikan kamu menghindari hal-hal berikut:
1. Melupakan Exclude Files
Jika kamu tidak mengatur matcher dengan benar, middleware akan mencoba memproses file seperti favicon.ico atau _next/static. Ini bisa menyebabkan bug aneh atau penurunan performa.
2. Terjebak dalam Infinite Redirect Loop
Ini sering terjadi ketika kamu mengarahkan pengguna ke /login, tapi middleware kamu juga memproses rute /login tersebut dan menemukan bahwa pengguna belum login, lalu mengarahkannya lagi ke /login. Selalu beri pengecekan: if (pathname === '/login') return NextResponse.next();.
3. Mengandalkan Node.js API Spesifik
Ingat, middleware berjalan di Edge Runtime, bukan environment Node.js penuh. Kamu tidak bisa menggunakan modul seperti fs (file system) atau crypto bawaan Node.js yang lama. Pastikan library yang kamu gunakan kompatibel dengan Edge.
Kesimpulan
Implementasi Middleware di Next.js adalah langkah krusial untuk membangun aplikasi web profesional yang aman dan terstruktur. Dengan memusatkan logika autentikasi dan otorisasi di satu titik, kamu tidak hanya menghemat waktu pengembangan tetapi juga mengurangi risiko celah keamanan di aplikasi kamu.
Mulai sekarang, cobalah audit aplikasi Next.js kamu. Apakah masih ada pengecekan login yang berulang-ulang di setiap halaman? Jika iya, mungkin ini saat yang tepat bagi kamu untuk memindahkannya ke middleware.
Selamat mencoba dan happy coding! Jika kamu menemui kesulitan, jangan ragu untuk bereksperimen dengan dokumentasi resmi Next.js atau mencoba implementasi sederhana terlebih dahulu sebelum masuk ke logika yang kompleks.
Komentar