Looking to build a site or app that pulls in content on the fly—without constant redeploys or endless copy-paste? This guide’s for you. If you’re tired of wading through vague docs and just want a straight answer on how to use Contentful’s GraphQL API to deliver dynamic content, you’re in the right place. I’ll walk you through the nuts and bolts and call out what actually matters (and what you can skip).
Why bother with Contentful’s GraphQL API?
Contentful is a headless CMS that lets you manage content in one place and serve it anywhere. The GraphQL API is the main way to fetch exactly what you need—no more, no less. Unlike REST, you ask for the fields you want and get back just that. In practice, this means faster sites, less wasted bandwidth, and (if you do it right) a less brittle frontend.
But let’s be real: Contentful’s API can be confusing at first, and a lot of tutorials skip over the real-world snags. Let’s dig in.
Step 1: Set Up Your Contentful Space and Content Model
Before you can query anything, you need to have some content in Contentful. If you’ve already got a space and content, skip to Step 2.
- Create a free Contentful account and make a new “space” (think: project).
- Define your content model: Add a Content Type (like
BlogPost
), give it fields (liketitle
,body
, maybe animage
). - Add a few entries so you’ve got something to fetch.
- Publish your entries. This trips people up—unpublished content won’t show up via the API.
Pro tip: Keep your first content model simple. You can always add more fields later.
Step 2: Get API Access—Grab Your Space ID and API Key
To use the GraphQL API, you’ll need two things:
- Space ID: Find this in your Contentful space settings.
- Content Delivery API Key: Go to Settings > API keys, and create a key if you don’t have one.
Note: The GraphQL API is for published content only. Drafts are invisible unless you use the Preview API, which is a different endpoint and key. For most public sites, you want the published stuff.
Step 3: Understand the GraphQL Endpoint
Here’s the endpoint you’ll use for published content:
https://graphql.contentful.com/content/v1/spaces/
If you’re using environments (like “master” or “staging”), tack on environments/<ENVIRONMENT_ID>
like this:
https://graphql.contentful.com/content/v1/spaces/
Don’t overcomplicate environments. If you don’t know what that is, you’re probably fine sticking with the default “master” environment.
Step 4: Make Your First Query (with Curl or Postman)
Let’s test the waters before wiring up your frontend.
Example GraphQL Query
Suppose you’ve got a content type called BlogPost
with fields title
and body
.
Here’s a sample query:
graphql query { blogPostCollection(limit: 5) { items { title body } } }
Make the Request with curl
bash
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer
Replace <YOUR_CDA_ACCESS_TOKEN>
and <SPACE_ID>
with your real values.
If you get errors:
- Check your content type name. Contentful auto-generates GraphQL types from content types—so BlogPost
becomes blogPostCollection
.
- Make sure entries are published.
- Double-check your access token and space ID.
Step 5: Build a Real Query—Handling Links, Images, and Rich Text
You’ll probably want more than titles and bodies. Here’s how to fetch:
Linked Entries (References)
If a BlogPost
has an author
field that references an Author
type:
graphql query { blogPostCollection { items { title author { name profilePicture { url } } } } }
Images
To get images, you’ll want the url
field. You can also fetch width, height, and description.
graphql imageField { url description width height }
Tip: You can add image transformations (resizing, cropping) with URL parameters. Contentful docs cover this, and it actually works well.
Rich Text Fields
Rich text is stored as JSON, not HTML. If you want to render it, you’ll need a parser (like @contentful/rich-text-react-renderer
for React, or write your own).
graphql body { json }
Don’t try to render the raw JSON. You’ll need a renderer or you’ll end up with a wall of curly braces.
Step 6: Wire It Up in Your Frontend
Let’s say you’re using JavaScript (React, Next.js, etc.). Here’s the no-nonsense way to fetch data:
Fetching with fetch API
js const SPACE_ID = 'your-space-id'; const ACCESS_TOKEN = 'your-access-token';
async function fetchBlogPosts() {
const res = await fetch(
https://graphql.contentful.com/content/v1/spaces/${SPACE_ID}
,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: Bearer ${ACCESS_TOKEN}
,
},
body: JSON.stringify({
query: query {
blogPostCollection {
items {
title
body { json }
}
}
}
,
}),
}
);
const { data, errors } = await res.json();
if (errors) {
console.error(errors);
return [];
}
return data.blogPostCollection.items;
}
What about GraphQL clients?
You can use urql
, Apollo Client
, or similar if you want caching, fragments, etc. For simple projects, built-in fetch
is usually easier and less overhead.
Step 7: Avoid the Gotchas
Here’s what trips people up (and how to dodge it):
- Content model changes = breaking changes. If you rename a field in Contentful, the GraphQL schema changes. This can break your queries. Don’t change field IDs lightly.
- API limits: The free plan has rate limits (usually fine for dev, but keep an eye on them for production).
- No filtering on all fields. You can filter on some fields but not all. For complex queries, you might need to fetch more and filter in code.
- Preview vs. Delivery API: The GraphQL Content Delivery API only serves published content. If you want to see drafts, use the Preview API (different URL and token).
- Localization: If you need multi-language content, you’ll need to specify the
locale
argument in your queries.
Step 8: Make Content Truly Dynamic
Dynamic content means you can update your Contentful entries and see updates in your app—no deploys needed.
- For static site generators (like Next.js with
getStaticProps
), you get fast, cached builds but need to rebuild to see new content. - For dynamic fetching (client-side or with serverless functions), your app always pulls the latest from Contentful.
Real talk:
Unless you have massive traffic or SEO needs, fetching content at runtime (SSR or client-side) is easiest. Static builds are faster, but you lose “true” dynamic updates unless you set up webhooks or incremental static regeneration.
What to Ignore (at First)
- Contentful SDKs: The official JavaScript SDK is for the REST API, not GraphQL. You don’t need it.
- Deep GraphQL features: Fragments, unions, etc. are cool, but start with simple queries.
- Over-architecting: Don’t build a complex caching layer until you have actual scale problems.
Wrapping Up
That’s the gist—fetch what you need, render it, and don’t sweat the fancy stuff. Contentful’s GraphQL API is powerful, but most projects don’t need more than basic queries and a simple fetch wrapper.
Keep your models simple, test queries in the GraphQL playground, and only add complexity when you need it. You’ll move faster and keep your sanity.