Pendahuluan
Ketika data tabel terus tumbuh, API Routes Next.js yang sebelumnya responsif bisa melambat karena query yang melakukan full table scan. Untuk mempercepatnya, langkah praktis pertama adalah memprofil query dan membaca execution plan, kemudian menambah index yang tepat dan membatasi data yang diambil dengan pagination atau cursor-based fetch.
Dalam artikel ini dijelaskan secara langsung cara tim engineer menangani kasus tersebut, termasuk bagaimana menambahkan logging durasi sederhana dan kapan melakukan review ulang statistik index.
Profiling Query dan Execution Plan
Langkah awal yang paling efektif adalah mengukur bagian mana dari API yang memperlambat permintaan. Untuk API Routes Next.js, tambahkan logging durasi di sekitar pemanggilan query database.
export default async function handler(req, res) {
const start = Date.now();
const result = await prisma.order.findMany({
where: { status: 'PAID' },
orderBy: { createdAt: 'desc' },
take: 100,
});
const duration = Date.now() - start;
console.log(`query duration: ${duration}ms`);
res.status(200).json(result);
}
Jika logging menunjukkan lonjakan durasi, gunakan EXPLAIN pada query yang sama untuk melihat execution plan. Cari operasi dengan tipe seq scan atau nested loop yang menandakan tidak ada index yang digunakan.
Contoh perintah SQL:
EXPLAIN SELECT * FROM orders WHERE status = 'PAID' ORDER BY created_at DESC LIMIT 100;
Perhatikan apakah DBMS menyertakan index scan. Jika tidak, berarti ada kebutuhan untuk menambah index.
Menambah Index Sekunder dan Komposit
Setelah execution plan mengungkapkan kolom yang sering dipakai dalam WHERE dan ORDER BY, tambahkan index yang relevan. Untuk kasus di atas, index pada (status, created_at) membantu membatasi dan mengurutkan data secara efisien.
Contoh SQL menambah index komposit:
CREATE INDEX idx_orders_status_created_at ON orders (status, created_at DESC);
Penting untuk menambahkan kolom secara urut sesuai pola query. Jika query hanya memfilter status, index tunggal pada status sudah membantu, tetapi kombinasi dengan created_at memastikan DBMS tidak melakukan operasi sort tambahan.
Perlu diperhatikan trade-off: index tambahan memperlambat write dan memakan ruang. Karena itu, review statistik index secara berkala sesuai pertumbuhan data, misalnya setiap kali tabel tumbuh 20-30% atau saat latency kembali naik.
Pagination dan Cursor-Based Fetch untuk Hindari Full Table Scan
Lebih baik memecah permintaan besar menjadi potongan kecil daripada menarik seluruh data sekaligus. Gunakan pagination berbasis cursor agar Next.js API Routes membaca data dalam batch yang dapat diprediksi.
const PAGE_SIZE = 50;
export default async function handler(req, res) {
const cursor = req.query.cursor;
const where = { status: 'PAID' };
const orders = await prisma.order.findMany({
where,
orderBy: { createdAt: 'desc' },
take: PAGE_SIZE + 1,
cursor: cursor ? { id: cursor } : undefined,
skip: cursor ? 1 : 0,
});
const nextCursor = orders.length > PAGE_SIZE ? orders[PAGE_SIZE - 1].id : undefined;
res.status(200).json({ data: orders.slice(0, PAGE_SIZE), nextCursor });
}
Cara ini menghindari full table scan karena DBMS hanya mengambil sejumlah baris sesuai limit. Selain itu, cursor memastikan urutan deterministik dan meminimalkan risiko melewatkan data saat ada insert simultan.
Monitoring Sederhana dan Review Index
Setelah index dan pagination diterapkan, terus pantau durasi query dengan logging atau metrics sederhana. Catat kasus di mana durasi melebihi ambang batas (misalnya 300ms). Ketika log menunjukkan pergeseran pola, jalankan kembali profiling dan EXPLAIN untuk melihat apakah index masih optimal.
Seiring tabel tumbuh, statistik index dapat menjadi tidak akurat. Jadwalkan review index berdasarkan volume data, misalnya setiap kali ada peningkatan 20% di jumlah baris atau ketika rata-rata durasi meningkat dari baseline.
Gunakan perintah seperti ANALYZE untuk memperbarui statistik dan REINDEX bila index terfragmentasi. Pastikan maintenance dijadwalkan saat traffic rendah agar tidak mengganggu layanan.
Kesimpulan
Optimasi query Next.js API Routes memerlukan pendekatan menyeluruh: mulai dari memprofil query, membaca execution plan, menambah index yang tepat, hingga membatasi data dengan pagination atau cursor-based fetch. Tambahkan logging durasi sederhana untuk monitoring, dan ulangi review index ketika data terus bertambah. Pendekatan ini menjaga responsivitas API meskipun volume data bertambah pesat.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!