This episode covers data fetching in Astro: fetching at build time in frontmatter, integrating external APIs and the content layer, server-side rendering with an SSR adapter, and progressive and partial hydration for dynamic pages.

Content sites rarely stand alone — they often need data from external APIs, databases, or CMS services. Episode 6 covers how Astro fetches data: when data is fetched at build time, when it is fetched per request with server-side rendering (SSR), and how both modes can coexist.
The important concept you will learn is the time of data fetching. In static mode, data is fetched once at build and the result is locked into the HTML. In SSR mode, data is fetched on every request so it is always fresh. Each has trade-offs you need to understand.
This episode also introduces the Astro 5 content layer, which unifies Markdown sources, remote APIs, and databases into a single collection API.
Because frontmatter runs at build time, you can use fetch right there. Example of fetching users from an API:
---
interface Pengguna {
id: number;
name: string;
}
const res = await fetch("https://api.contoh.dev/pengguna");
const pengguna: Pengguna[] = await res.json();
---
<h1>Daftar Pengguna</h1>
<ul>
{pengguna.map((u) => <li key={u.id}>{u.name}</li>)}
</ul>The await fetch("...") code runs once during npm run build. The resulting HTML is locked until the next build — suitable for data that rarely changes.
If you want each data item to become its own page, combine it with getStaticPaths. This is analogous to getStaticProps in other frameworks: the item list is fetched once, then each item is rendered into a separate static page.
---
export async function getStaticPaths() {
const res = await fetch("https://api.contoh.dev/artikel");
const artikel = await res.json();
return artikel.map((item) => ({ params: { slug: item.slug } }));
}
const { slug } = Astro.params;
---
<h1>Artikel {slug}</h1>Astro 5 introduces the content layer API, which unifies various data sources into a single collection. The collection definition is extended with a glob loader for local files or a custom loader for remote sources:
import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
});With the content layer, getCollection("blog") stays the same, but the source can be flexible — from local files to remote APIs. Episode 11 will use this pattern for a CMS.
Build-time fetch must be resilient. If the API goes down, the build could fail. Get into the habit of checking the response status and providing a fallback:
const res = await fetch("https://api.contoh.dev/data");
if (!res.ok) {
console.error("Gagal mengambil data:", res.status);
return [];
}
const data = await res.json();For data that must always be fresh, enable server mode. First install an adapter — for example for Node.js:
npx astro add nodeThen set the output in astro.config.mjs:
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
export default defineConfig({
output: "server",
adapter: node({ mode: "standalone" }),
});With output: "server", every request is rendered on the server. .astro files can now read the request directly:
---
const cari = Astro.url.searchParams.get("q") ?? "";
const res = await fetch(`https://api.contoh.dev/cari?q=${cari}`);
const hasil = await res.json();
---
<h1>Hasil pencarian: {cari}</h1>
<ul>
{hasil.map((item) => <li>{item.title}</li>)}
</ul>In the code above, Astro.url.searchParams is only available in server mode — in static mode the value is already locked at build time.
Astro supports hybrid mode: static pages by default, but some pages rendered on the server. Set prerender = true in the frontmatter of static pages, or let pages render on the server. The output: "server" prop makes most pages server-rendered, and prerender = true excludes specific pages.
---
export const prerender = true;
---
<h1>Halaman ini tetap dibangun saat build time</h1>The rule of thumb:
Partial hydration in episode 7 will complete this picture: content can be fetched at any time, but JavaScript is only shipped to the interactive parts.
Info
Server mode requires a Node runtime on your hosting. For purely static hosting such as Cloudflare Pages' static tier, server pages will not run — episode 20 covers choosing the right hosting.
Episode 6 opens up the two ways Astro fetches data: at build time with fetch in frontmatter and getStaticPaths, and at request time with SSR mode and an adapter. The content layer API unifies local and remote data sources, and hybrid mode combines both in a single project.
The key takeaways:
getStaticPaths generates many static pages from one API.npx astro add node enables server mode with an adapter.Astro.url.searchParams only works in server mode.In the next episode 7, we will cover interactivity and hydration: partial hydration with client:load, client:idle, and client:visible, using React, Vue, Svelte, or Solid components, and optimizing the JavaScript bundle. This is the key to keeping your site interactive without losing speed.