Almost EVERYONE who tried headless systems said they saw benefits. Download the state of CMS now!

O’Reilly Report: Decoupled Applications and Composable Web Architectures - Download Now

Empower your teams & get a 582% ROI: See Storyblok's CMS in action

Skip to main content

GraphQL Content Delivery API

Beside the traditional REST API you can also use Storyblok with GraphQL, which offers a number of advantages like automated documentation and strongly typed responses. The API is a read-only endpoint and optimized for fast content delivery.

Section titled Endpoint Endpoint

You can use the following endpoint to send GraphQL requests to this URL:

https://gapi.storyblok.com/v1/api

If you use other regions, you need to append the region code. Example:

https://gapi-us.storyblok.com/v1/api

The GraphQL endpoint is read-only. You can use our Management API for those operations if you want to write, update, or delete your content or migrate from another solution.

Section titled Authentication Authentication

To communicate with the GraphQL endpoint, you will need to send an API token in the request header Token. You can find your read-only API token in the Space Settings area, in the Access Tokens tab:

Screenshot of the Access Tokens tab in the Settings Menu
  • Public: Allows access to your published content entries: version=published results in 403 on version=draft
  • Preview: Allows access to the draft and published content entries: version=draft and version=published

Section titled Rate limits Rate limits

Storyblok's GraphQL API limit is different from the REST API limit.

Why are the API rate limits different? With GraphQL, one GraphQL call can replace multiple REST calls. A single complex GraphQL call could be the equivalent of thousands of REST requests.

To accurately represent the server cost of a query, the Storyblok's GraphQL API calculates a call's rate limit score based on a normalized scale of points. A query's score factors in first and last arguments on a parent connection and its children.

The formula uses the first and last arguments on a parent connection and its children to pre-calculate the potential load on Storyblok's systems.

Each new connection has its own point value. Points are combined with other points from the call into an overall rate limit score.

Storyblok's GraphQL API rate limit is 100 points per second. Note that 100 points per second is not the same as 100 calls per second: the GraphQL API and REST API use different rate limits.

If the GraphQL API reaches 100 points per second it will return the status code 429 which means you can retry your call with a delay later.

The limit for resolve_links per story is 100 when resolving with url.

Section titled Estimate the cost of a query Estimate the cost of a query

There is a GraphQL object called RateLimit with a property called maxcost that will return the maximum cost of a single request with that specific query you are performing.

For example, if the maxcost of your query is 3 and you are performing 10 requests, you will not spend 30 credits because 3 is just an upwards approximation. Cached requests won't have any cost.

Going back to the previous example where the maxcost is 3, in order to calculate the number of requests you can perform per second you need to divide 100 by 3. In this case, you can safely perform 33 requests per second.

If you rely on the maxcost you will always make sure that you are not hitting the limit and that your requests won't get the 429 status as a response.

Section titled Send your first request Send your first request

To try out the GraphQL endpoint you can send a basic curl request with your bash console.

$ curl 'https://gapi.storyblok.com/v1/api' \
    -H 'Token: YOUR_PREVIEW_TOKEN' \
    -H 'Version: draft' \
    --data-binary '{ "query": "query { PageItems { items { name } } }" }'

Section titled GraphQL Playground GraphQL Playground

To see which queries are available you can use our GraphQL Browser. Just exchange YOUR_TOKEN with your API token and you can play around with the API:

https://gapi-browser.storyblok.com?token=YOUR_TOKEN

You also need to do the same for playgrounds, for example, adding the {-us} for US regions:

https://gapi-us-browser.storyblok.com

Section titled How to fetch content items How to fetch content items

Section titled Generated fields Generated fields

Storyblok's GraphQL engine generates a field for each content type you define by using the pascal case version of the component name. For every content type Storyblok generates two fields.

  • One for receiving a single item: [Pascal-cased Name] Item
  • And one for receiving a multiple items: [Pascal-cased Name] Items

Examples:

  • page gets converted to PageItem and PageItems
  • blog-article gets converted to BlogArticleItem and BlogArticleItems

Section titled Query a single record Query a single record

To query a single content item use the pascal-cased version of your content type name and provide an id as filter attribute which can be the id, uuid or full_slug of the item.

Following an example which shows how to get the content item with "home" as full_slug:

query {
  PageItem(id: "home") {
    name
    content {
       _uid
       component
    }
  }
}

Section titled Query multiple records Query multiple records

To query multiple content items, you can use the pascal-cased version of your content type followed by the keyword Items. There are the same filters available as from the REST API endpoint. Please check the REST API documentation for the filter options you have.

Following is an example of how to query the content type product and filter the result to show only items from a specific folder:

query {
  ProductItems(starts_with: "products/") {
    items {
      id
      name
    }
    total
  }
}

IMPORTANT :

Note that the cached_url property in the link field is camelCased on the GraphQL API and not snake_cased, which is the case with Storyblok CDN API.


Section titled Use the language parameter to get specific content in Queries Use the language parameter to get specific content in Queries

To get a specific language, you'd need to use the parameter starts_with. when it is an item, use the full_slug with the language code prefixed. The language parameter is used for the relations and in the case that you want to get a story by its ID.

{
  RelationsItems(starts_with: "en-ca/*") {
    total
    items {
      content {
        optionfield1(language: "en-ca") {
          lang
        }
      }
      lang
    }
  }
  Space {
    languageCodes
  }
}

Another example can be found below:

{
  RelationsItem(id: "en-ca/examplefolder/subfolder2/relations8") {
    lang
    id
    content {
      optionfield1(language: "en-ca") {
        id
        lang
      }
    }
  }
}

If you're using an id, use the language parameterbas shown below;

 {
  RelationsItem(id: "64499188", language: "en-ca") {
    lang
    id
    content {
      optionfield1(language: "en-ca") {
        id
        lang
      }
    }
  }
}

Section titled Pagination Pagination

When querying content items, you can supply arguments that allow you to paginate the query response. Pagination allows you to request a certain amount of records at the same time. The default limit is 25 content items, and the maximum is 100. With the attribute total you can get the total number of items which is useful for creating pagination links.

Use per_page to limit the number of results:

{
  ProductItems(per_page: 5) {
    items {
      name
    }
    total
  }
}

By providing the page argument, you can go to a specific offset (page is multiplied by the default per_page value 25):

{
  ProductItems(page: 2) {
    items {
      name
    }
    total
  }
}

Section titled GraphQL Filter Query Examples GraphQL Filter Query Examples

Here are some examples of our GraphQL filter query.

  • To filter your entries for all components with names like "author" and sort them in ascending order.
            
          {
      ContentNodes(sort_by: "position:asc", filter_query: {component: {like: "author"}}) {
        total
        items {
          content
        }
      }
    }
        
  • To filter your entries for components with content-type “page”.
            
          {
      ContentNodes(filter_query: {component: {in: "page"}}) {
        total
        items {
          content
        }
      }
    }
        
  • To filter your entries by checking if a custom array attribute contains one of the values provided.
            
          {
      ContentNodes(filter_query: {categories: {in_array: "9aa72a2f-04ae-48df-b71f-25f53044dc97,84550816-245d-4fe6-8ae8-b633d4a328f4"}}) {
        total
        items {
          content
        }
      }
    }
        
https://gapi-browser.storyblok.com/?token={PREVIEW_TOKEN}

You can learn more about our GraphQL endpoint with our tutorial which uses GraphQL, React, and Apollo: storyblok.com/tp/storyblok-graphql-react-apollo

Section titled Resolve relations in GraphQL Resolve relations in GraphQL

With Storyblok GraphQL endpoint, you can resolve the relationship between content entries using the story id and specifying the relationship, as seen below.


        
      {
  PageItem(id: {STORY_ID}, resolve_relations: {RELATIONSHIP}) {
    id
    content {
      body
      component
    }
  }
}
    

HINT:

To explore the Storyblok GraphQL functionalities, use the playground at this URL: https://gapi-browser.storyblok.com/?token=insert-here-your-access-token