Biar Next.js Cepat Ini Trik Paling Gampang Optimasi Data Fetching di App Router

Biar Next.js Cepat Ini Trik Paling Gampang Optimasi Data Fetching di App Router
Photo by Szabo Viktor/Unsplash

Next.js itu memang game changer. Bayangin, kita bisa bikin aplikasi full-stack yang super cepat tanpa perlu pusing mikirin konfigurasi server yang ribet. Tapi, secepat-cepatnya Next.js, kalau kita salah strategi dalam mengambil data (data fetching), performa aplikasi bisa langsung melambat drastis. Rasanya kayak naik mobil sport tapi pakai rem tangan.

Apalagi sejak Next.js merilis App Router. Ini bukan cuma update, tapi pergeseran paradigma total, khususnya soal di mana dan bagaimana kita mengambil data. Kalau kamu masih pakai cara lama dari Pages Router, siap-siap saja performa aplikasi kamu nggak maksimal.

Artikel ini bukan tentang trik coding yang super kompleks. Ini tentang trik-trik paling gampang dan paling impactful yang bisa langsung kamu terapkan di App Router biar Next.js kamu lari kencang. Kita fokus pada optimasi data fetching, karena ini seringkali jadi biang keladi loading time yang lama.

1. Pahami Prinsip Dasar: Server Components Adalah Tempat Data Fetching

Ini adalah trik paling fundamental yang wajib kamu kuasai di App Router. Sebelum ngobrol soal optimasi, kamu harus ubah mindset dulu.

Dulu di Pages Router, kita sering banget pakai getServerSideProps atau getStaticProps, tapi sebagian besar data fetching tetap dilakukan di Client Component menggunakan useEffect dan library seperti SWR atau React Query. Nah, di App Router, filosofinya berubah total: Data Fetching adalah pekerjaan Server Component.

Kenapa ini trik paling gampang?

Server Component (SC) berjalan di sisi server, sebelum HTML dikirim ke browser. Ketika kamu melakukan fetch data di SC:

  1. Zero-Bundle Overhead: Kode fetching data, library database, atau bahkan kredensial API kamu, semuanya nggak akan pernah sampai ke browser. Ukuran bundle JavaScript di klien jadi jauh lebih kecil dan loading lebih cepat.
  2. Latency Minimized: Server Next.js kamu (yang biasanya berada di region yang sama dengan API backend atau database) mengambil data secara lokal. Latensi antara server Next.js dan API kamu jauh lebih rendah daripada latensi antara browser pengguna di Indonesia dan server API di US.

Trik Aplikasinya: Pastikan semua komponen utama (yang nggak butuh interaksi state dari browser) dibuat sebagai Server Component (ini default-nya di App Router). Hanya gunakan use client ketika benar-benar perlu state interaktif seperti form handling atau event listener.

Kalau kamu perlu data di Client Component, sebisa mungkin kirim data tersebut sebagai props dari Server Component "induk"-nya. Jangan lakukan fetch di Client Component kecuali itu memang data yang sangat dinamis dan perlu di-refetch setelah interaksi pengguna.

2. Manfaatkan Native fetch() dan Built-in Caching Next.js

Di App Router, fungsi fetch standar JavaScript telah diperkaya (di-extended) oleh Next.js. Ini adalah senjata rahasia paling ampuh untuk optimasi tanpa perlu install library tambahan.

Next.js secara otomatis melakukan dua hal luar biasa pada fetch() yang kamu panggil di Server Component:

A. Automatic Request Memoization (De-duping)

Bayangkan kamu punya dua komponen anak yang berbeda, tapi keduanya butuh data yang sama (misalnya, data profil pengguna).

javascript
// Server Component utama
async function Page() {
  // Panggilan fetch yang sama (Endpoint A)
  const userData1 = await fetchUser(); 
  const userData2 = await fetchUser(); 

Secara tradisional, kode di atas akan memanggil API dua kali. Tapi di App Router, Next.js otomatis mendeteksi bahwa URL dan opsi fetch di kedua panggilan itu sama persis dalam satu render Request-Response lifecycle. Hasilnya? Next.js hanya akan memanggil API satu kali dan membagikan hasilnya ke dua variabel tersebut. Ini yang disebut Automatic Memoization atau De-duping.

Trik Aplikasinya: Jangan takut memanggil fetch di banyak tempat selama itu di dalam Server Component. Next.js sudah cukup pintar untuk menghindari panggilan ganda.

B. Cache Default yang Cerdas

Next.js 13+ App Router secara default akan menganggap hasil fetch kamu sebagai data yang dapat di-cache (mirip dengan getStaticProps di Pages Router).

Default Behavior: Next.js akan menggunakan cache: 'force-cache' secara implisit. Data akan di-cache (disimpan) selamanya di server dan hanya akan di-revalidate saat kamu melakukan deployment* baru. Jika Data Kamu Statis (Jarang Berubah): Biarkan saja default*nya. Ini adalah performa tercepat!

Tapi, bagaimana kalau datanya dinamis? Misalnya data harga saham atau skor live. Kamu bisa memaksanya untuk menjadi dinamis dengan dua cara mudah:

  1. Opting-out of Caching (no-store):
javascript
    const res = await fetch('https://api.example.com/live-data', { cache: 'no-store' });

Menggunakan cache: 'no-store' berarti data akan selalu di-fetch setiap kali pengguna me-request halaman tersebut (mirip getServerSideProps).

  1. Time-based Revalidation (revalidate):
javascript
    const res = await fetch('https://api.example.com/news', { next: { revalidate: 60 } });

Ini adalah mode Incremental Static Regeneration (ISR) versi App Router. Data akan di-cache selama 60 detik. Setelah 60 detik, request pertama yang masuk akan memicu fetch baru di background sambil tetap menyajikan data lama, memastikan pengguna selalu mendapatkan response yang cepat.

Trik Aplikasinya: Selalu pakai fetch() bawaan. Hindari axios atau library HTTP lainnya di Server Component jika kamu ingin memanfaatkan sistem caching dan revalidation Next.js yang canggih ini.

3. Hancurkan "Waterfall" dengan Parallel Data Fetching

Ini adalah salah satu kesalahan paling umum yang menyebabkan loading lambat, dan trik untuk mengatasinya sangat mudah: Jangan biarkan request data menunggu request data lainnya selesai.

Ini yang disebut "Waterfall":

javascript
// Contoh data fetching waterfall (Buruk!)
async function Page() {
  // 1. Ambil ID Pengguna (1 detik)
  const user = await fetch('/api/user'); 
  
  // 2. TUNGGU user selesai, baru ambil Postingan (1.5 detik)
  const posts = await fetch(/api/posts/${user.id}); 

Waktu tunggu total menjadi penjumlahan dari semua request yang berurutan.

Trik Paling Gampang: Gunakan Promise.all()

Jika dua atau lebih data fetching tidak saling bergantung, jalankan semuanya secara bersamaan (paralel).

javascript
// Contoh Parallel Data Fetching (Cepat!)
async function Page() {
  const [userPromise, productPromise] = await Promise.all([
    fetch('/api/user'), // Start fetching user
    fetch('/api/products') // Start fetching products
  ]);

Dengan Promise.all(), loading time kamu tereduksi drastis karena request berjalan secara simultan.

Bagaimana jika ada yang bergantung? (Partial Waterfall)

Jika data postingan memang bergantung pada ID pengguna, kamu bisa membagi fetch menjadi dua tahap:

javascript
async function Page() {
  // Tahap 1: Hanya data yang tidak bergantung
  const userPromise = fetch('/api/user');
  const categoriesPromise = fetch('/api/categories');
  
  // Tunggu userPromise selesai untuk mendapatkan ID
  const userRes = await userPromise;
  const user = await userRes.json(); 

Meskipun terlihat lebih panjang, cara ini memastikan CPU server kamu tidak hanya "diam" menunggu satu request selesai, melainkan langsung memulai request lainnya di background.

4. Gunakan loading.js dan Suspense untuk Streaming

Konsep data fetching di Server Component itu keren, tapi ada satu masalah: Selama semua data di halaman belum ter-fetch di server, browser pengguna nggak akan melihat apa-apa (blank screen).

Next.js 13+ App Router menyelesaikan ini dengan fitur Streaming menggunakan Suspense dari React. Dan trik paling gampang untuk menggunakannya adalah dengan file spesial loading.js.

A. Power of loading.js

Ketika kamu membuat file loading.js di dalam folder route (misalnya app/dashboard/loading.js), Next.js akan secara otomatis membungkus konten di folder dashboard tersebut dengan Suspense boundary.

Cara Kerjanya: Begitu pengguna me-request /dashboard, Next.js langsung mengirimkan HTML halaman dan placeholder dari loading.js terlebih dahulu, sementara proses data fetching* di Server Component tetap berjalan di server. Result: Pengguna langsung melihat skeleton screen atau spinner (UX yang baik) dan tidak melihat blank screen* selama 2-3 detik.

Ketika data sudah selesai diambil di server, Next.js akan secara otomatis mengirimkan potongan HTML yang berisi konten sesungguhnya, menggantikan loading state tersebut.

Trik Aplikasinya: Selalu buat loading.js untuk route yang memiliki data fetching berat. Ini bukan trik performa (data tetap di-fetch selama waktu yang sama), tapi ini adalah trik perceived performance (performa yang dirasakan), yang sama pentingnya untuk pengalaman pengguna.

B. Menggunakan Suspense Manual

Jika kamu ingin menerapkan loading state hanya pada bagian tertentu dari halaman (bukan seluruh halaman), kamu bisa menggunakan komponen Suspense secara manual.

javascript
// Di Server Component utama
import { Suspense } from 'react';export default function Page() {
  return (
    
      Selamat Datang!
      {/ Hanya bagian yang lambat yang di-suspend /}
      }>
         
      
      {/ Bagian lain yang cepat tetap ditampilkan /}
      
    
  );
}

Di sini, RecentPosts adalah komponen async Server Component yang melakukan data fetching lambat. Dengan membungkusnya dalam Suspense, elemen FooterComponent dan

akan dikirimkan duluan, sementara RecentPosts menunggu datanya siap.

5. Gunakan Route Handler untuk Keamanan dan Abstraksi

Ada kalanya kamu harus berinteraksi dengan API pihak ketiga (Third-Party API) atau database yang sensitif. Meskipun Server Component sudah cukup aman karena kodenya hanya berjalan di server, membuat data fetching yang kompleks bisa membuat kode Server Component kamu jadi terlalu ramai.

Triknya: Pindahkan logic yang kompleks atau sensitif ke Route Handler.

Route Handler adalah pengganti API Routes di Pages Router, yang memungkinkan kamu membuat endpoint API custom di dalam folder app/api/.

Contoh Kasus Penggunaan:

Misalnya kamu harus mengambil data dari database, memprosesnya, dan melakukan validasi otorisasi.

  1. Server Component (Sederhana):
javascript
    // app/dashboard/page.js
    async function Page() {
      // Panggil Route Handler internal, bukan API eksternal
      const res = await fetch('http://localhost:3000/api/dashboard-summary'); 
      const summary = await res.json();
      // ... render UI
    }
  1. Route Handler (Logic Kompleks):
javascript
    // app/api/dashboard-summary/route.js
    import { getServerSession } from "next-auth"; 
    
    export async function GET(request) {
      // 1. Lakukan otorisasi sensitif
      const session = await getServerSession();
      if (!session) return new Response('Unauthorized', { status: 401 });
      
      // 2. Ambil data dari database/API pihak ketiga
      const data = await db.fetchComplexData(session.user.id);

Keuntungan Trik Ini:

  • Keamanan: Kredensial database atau kunci API pihak ketiga bisa diisolasi dan tidak perlu ada di file page.js.
  • Abstraksi: Kode di Server Component (page.js atau layout.js) menjadi jauh lebih bersih dan fokus hanya pada rendering, sementara Route Handler fokus pada logika bisnis dan data fetching.

Fleksibilitas: Kamu bisa memanggil Route Handler ini baik dari Server Component (paling cepat karena request*nya internal) maupun dari Client Component.

Kesimpulan: Next.js Cepat Dimulai dari Strategi Fetching

Optimasi data fetching di App Router itu jauh lebih mudah daripada di Pages Router, asalkan kamu mematuhi beberapa aturan main baru.

Intinya adalah:

  1. Pindahkan sebanyak mungkin fetch ke Server Component. Jaga Client Component se-bersih mungkin dari urusan data.
  2. Percaya pada fetch() bawaan Next.js. Manfaatkan Automatic Memoization dan konfigurasi cache serta revalidate untuk mengontrol seberapa sering data kamu diperbarui.
  3. Gunakan Promise.all() untuk paralel fetching. Hindari waterfall yang membuang waktu tunggu server.
  4. Terapkan loading.js dan Suspense. Berikan ilusi kecepatan kepada pengguna sementara data berat sedang disiapkan di background.

Dengan menerapkan trik-trik gampang ini, performa aplikasi Next.js App Router kamu pasti akan meroket, memberikan pengalaman yang smooth dan cepat bagi pengguna. Selamat coding!