Skip to main content
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.

Caching and Performance

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.