Rute dinamis & getStaticPaths
getStaticPaths adalah tempat kamu memutuskan berapa banyak halaman yang dirender saat build. Untuk portal berita dengan arsip besar, keputusan itu punya konsekuensi langsung ke lama build dan ke biaya.
Intisari
getStaticPaths()wajib ada di rute dinamis yang di-prerender, dan dilarang ada di rute on-demand.- Ia berjalan sekali saat build. Artikel yang terbit setelah build tidak punya halaman sampai kamu build ulang.
- Untuk arsip 200 ribu artikel, prerender semuanya berarti build berjam-jam. Pola yang benar: prerender yang panas, on-demand untuk sisanya.
propsdarigetStaticPathsmenghindari kueri kedua per halaman โ ambil datanya sekalian di sana.paginate()menangani halaman daftar, termasuk menghitung total dan tautan sebelumnya/berikutnya.
Bentuk dasarnya
---
// src/pages/berita/[slug].astro
import type { GetStaticPaths } from "astro";
import { ambilSemuaSlugTerbit, ambilArtikelPublik } from "@/lib/artikel";
export const getStaticPaths: GetStaticPaths = async () => {
const artikel = await ambilSemuaSlugTerbit();
return artikel.map((a) => ({
params: { slug: a.slug },
props: { artikel: a }, // ikut terkirim, tidak perlu kueri lagi
}));
};
const { artikel } = Astro.props;
---
<h1>{artikel.judul}</h1>
Nama kunci di params harus sama persis dengan nama di kurung siku pada nama berkas. Berkas
[slug].astro butuh params: { slug: โฆ }. Salah nama tidak menghasilkan error yang
jelas โ halamannya cuma tidak pernah dibuat.
Pakai props, bukan kueri ulang. Tanpa props, tiap halaman akan
memanggil ambilArtikelPublik(slug) sendiri โ 200.000 halaman berarti 200.001 kueri.
Dengan props, datanya sudah ikut dari kueri pertama. Ini perbedaan antara build lima menit
dan build lima puluh menit.
Keputusan yang sebenarnya: berapa yang di-prerender?
Portal berita punya distribusi trafik yang sangat timpang. Beberapa ribu artikel terbaru menyerap hampir seluruh pembaca; ratusan ribu artikel arsip diakses sesekali dari pencarian. Merender semuanya saat build berarti membayar waktu build untuk halaman yang mungkin tidak dibuka sebulan sekali.
| Strategi | Waktu build | Artikel baru muncul | Cocok untuk |
|---|---|---|---|
| Prerender semua | Jam-jaman di arsip besar | Setelah build berikutnya | Situs < 5.000 halaman |
| On-demand semua | Detik | Langsung | Portal berita dengan CDN di depan |
| Hibrida | Menit | Langsung untuk yang on-demand | Kalau butuh jaminan latensi di halaman tertentu |
Untuk kasusmu, on-demand + cache Cloudflare hampir selalu jawabannya. Halaman dirender sekali saat pembaca pertama membukanya, lalu dilayani dari edge untuk semua orang berikutnya sampai TTL habis atau kamu purge tag-nya. Hasil akhirnya menyerupai statis dari sisi pembaca, tapi artikel baru langsung hidup tanpa build ulang โ dan itu syarat mutlak untuk redaksi yang menerbitkan puluhan artikel per hari. Mekanismenya di Fase 8.
Hibrida: prerender yang panas saja
---
// Prerender 500 artikel terbaru; sisanya dirender saat diminta.
export const prerender = true;
export const getStaticPaths: GetStaticPaths = async () => {
const panas = await ambilArtikelTerbaru("semua", 500);
return panas.map((a) => ({ params: { slug: a.slug }, props: { artikel: a } }));
};
---
Ini butuh output: "server" plus fallback yang sesuai di adapter โ dan jujur saja,
kerumitannya jarang sepadan kalau CDN-mu sudah bekerja. Ketahui bahwa polanya ada, lalu pilih on-demand
sampai ada pengukuran yang menuntut sebaliknya.
Paginasi
---
// src/pages/berita/[...halaman].astro โ /berita, /berita/2, /berita/3
import type { GetStaticPaths, Page } from "astro";
import { ambilSemuaTerbit } from "@/lib/artikel";
export const getStaticPaths: GetStaticPaths = async ({ paginate }) => {
const artikel = await ambilSemuaTerbit();
return paginate(artikel, { pageSize: 20 });
};
const { page } = Astro.props as { page: Page<Artikel> };
---
<h1>Berita โ halaman {page.currentPage} dari {page.lastPage}</h1>
<ul>
{page.data.map((a) => <li><a href={`/berita/${a.slug}`}>{a.judul}</a></li>)}
</ul>
<nav>
{page.url.prev && <a href={page.url.prev} rel="prev">โ Sebelumnya</a>}
{page.url.next && <a href={page.url.next} rel="next">Berikutnya โ</a>}
</nav>
Jangan paginasi seluruh arsip. ambilSemuaTerbit() di atas memuat seluruh tabel ke
memori โ di 200.000 artikel itu ratusan megabita, dan halaman ke-9.000 tidak pernah dikunjungi manusia,
hanya crawler. Batasi paginasi ke beberapa ratus halaman pertama dan alihkan sisanya ke pencarian.
Google juga tidak akan merayapi paginasi sedalam itu.
Beberapa parameter dan rute rest
---
// src/pages/perusahaan/[kode]/[tab].astro
export const getStaticPaths: GetStaticPaths = async () => {
const perusahaan = await ambilSemuaPerusahaan();
const tab = ["ringkasan", "keuangan", "pemegang-saham"];
return perusahaan.flatMap((p) =>
tab.map((t) => ({ params: { kode: p.kode, tab: t }, props: { perusahaan: p } })),
);
};
---
// Rute rest: [...jalur].astro
// /a/b/c โ Astro.params.jalur === "a/b/c"
// / โ Astro.params.jalur === undefined (bukan string kosong)
Perbedaan undefined dan string kosong itu nyata dan menggigit. params.jalur.split("/")
akan meledak di rute akar. Selalu tangani undefined-nya.
Kesalahan yang paling sering
| Gejala | Penyebab |
|---|---|
| Halaman 404 padahal datanya ada | Nama kunci params tidak sama dengan nama di kurung siku |
getStaticPaths ignored | Halaman itu prerender = false. Di rute on-demand, pakai Astro.params langsung |
| Build makan RAM sampai OOM | Seluruh tabel dimuat ke memori di getStaticPaths |
| Artikel baru tidak muncul | Halaman di-prerender; ia butuh build ulang |
Angka jadi "1", bukan 1 | params selalu string. Konversi sendiri, dan validasi hasilnya |
Latihan: buat berita/[slug].astro dengan getStaticPaths yang
mengembalikan 10 artikel palsu berikut props. Jalankan
pnpm build dan hitung berkas HTML di dist/berita/. Lalu hapus
props-nya dan ambil ulang data per halaman; tambahkan console.log di fungsi
pengambilan dan bandingkan berapa kali ia terpanggil di kedua versi. Terakhir, ubah halamannya jadi
prerender = false dan amati apa yang terjadi pada getStaticPaths.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.