How to use Contentful GraphQL API for dynamic content delivery

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 (like title, body, maybe an image).
  • 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//environments/

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 " \ --data '{ "query": "query { blogPostCollection(limit: 5) { items { title body } } }" }' \ https://graphql.contentful.com/content/v1/spaces/

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.