Astro components can fetch remote data to help generate pages at build time or on demand.
Using fetch() in Astro
All Astro components have access to the global fetch() function to make HTTP requests to APIs:
---
const response = await fetch('https://api.example.com/data');
const data = await response.json();
---
<h1>{data.title}</h1>
<p>{data.description}</p>
Top-level await
You can use top-level await in your component scripts:
---
import Contact from '../components/Contact.jsx';
import Location from '../components/Location.astro';
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
const randomUser = data.results[0];
---
<h1>User</h1>
<h2>{randomUser.name.first} {randomUser.name.last}</h2>
<Contact client:load email={randomUser.email} />
<Location city={randomUser.location.city} />
When Data is Fetched
For static builds, data is fetched once at build time. In SSR mode, data is fetched at runtime for each request.
If you need to re-fetch data client-side, use:
- A framework component with its own data fetching
- A client-side script
fetch() in Framework Components
The fetch() function is available in framework components:
import type { FunctionalComponent } from 'preact';
const data = await fetch('https://example.com/movies.json')
.then((response) => response.json());
const Movies: FunctionalComponent = () => {
return <div>{JSON.stringify(data)}</div>;
};
export default Movies;
GraphQL Queries
Use fetch() to query GraphQL APIs:
---
const response = await fetch(
"https://swapi-graphql.netlify.app/.netlify/functions/index",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `
query getFilm ($id:ID!) {
film(id: $id) {
title
releaseDate
}
}
`,
variables: { id: "ZmlsbXM6MQ==" },
}),
}
);
const json = await response.json();
const { film } = json.data;
---
<h1>Fetching information about Star Wars: A New Hope</h1>
<h2>Title: {film.title}</h2>
<p>Year: {film.releaseDate}</p>
Fetching from a Headless CMS
Astro components can fetch data from your CMS and render it as page content:
---
const response = await fetch('https://cms.example.com/api/posts');
const posts = await response.json();
---
<ul>
{posts.map(post => (
<li>
<a href={`/blog/${post.slug}`}>
<h2>{post.title}</h2>
</a>
</li>
))}
</ul>
Dynamic Routes from CMS Data
Use dynamic routes to generate pages based on CMS content:
---
export async function getStaticPaths() {
const response = await fetch('https://cms.example.com/api/posts');
const posts = await response.json();
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
---
<article>
<h1>{post.title}</h1>
<div set:html={post.content} />
</article>
Fetching Internal API Endpoints
You can construct URLs to internal endpoints using Astro.url:
---
const response = await fetch(new URL('/api/data', Astro.url));
const data = await response.json();
---
<div>{data.message}</div>
Passing Fetched Data as Props
Fetch data once and pass it to child components:
---
import BlogPost from '../components/BlogPost.astro';
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
---
{posts.map(post => (
<BlogPost
title={post.title}
content={post.content}
author={post.author}
/>
))}
Error Handling
Handle fetch errors gracefully:
---
let data = null;
let error = null;
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data = await response.json();
} catch (e) {
error = e.message;
}
---
{error && <p>Error: {error}</p>}
{data && <div>{data.content}</div>}
Authentication
Include authentication headers in your requests:
---
const response = await fetch('https://api.example.com/protected', {
headers: {
'Authorization': `Bearer ${import.meta.env.API_TOKEN}`,
'Content-Type': 'application/json',
}
});
const data = await response.json();
---
Store sensitive tokens in environment variables, not in your source code.
Static Build Caching
In static builds, fetched data is cached automatically as part of the build output.
SSR Caching
For SSR, implement your own caching strategy:
---
const cacheKey = 'my-data';
const cacheDuration = 60 * 60 * 1000; // 1 hour
let data = globalThis.cache?.get(cacheKey);
if (!data || Date.now() - data.timestamp > cacheDuration) {
const response = await fetch('https://api.example.com/data');
data = {
content: await response.json(),
timestamp: Date.now(),
};
if (!globalThis.cache) globalThis.cache = new Map();
globalThis.cache.set(cacheKey, data);
}
---
<div>{data.content.message}</div>
Working with CMS Integrations
See our CMS guides for specific integrations:
- Contentful
- WordPress
- Storyblok
- Sanity
- And more…
Each guide shows how to fetch and render content from that specific CMS.