GraphQL Delivery API (Alpha Preview)

The Contentful GraphQL API provides an experimental GraphQL interface to your published content.

Disclaimer

The GraphQL API is not ready for production use. It is under active development and lacks some basic functionalities including - but not limited - to the following:

  • Only published content is available.
  • Only content in the master environment of a space is available.
  • It is not optimized for performance and is not cached.
  • It is not Relay compatible

The schema structure is also likely to change too based on usage feedback.

During the alpha period, we will proceed with breaking changes to the API. We will announce these changes on our #alpha-graphql Slack community channel. This means that the API should NOT be used in production at this point, but can be tested and used with the goal of providing feedback and help us build a GraphQL API which best suits your needs.

Current limitations

The GraphQL API is in alpha, still in its early stages and under heavy development. It has the following limitations:

  • Only published content belonging to the master environment is available through this API.
  • Only the id equality filter is supported
  • The fallback locale mechanism is not supported
  • Page size for collections is subject to a limit of 10. See the Collections section for details.

Endpoint URL and authentication

The Contentful GraphQL API is available at

https://cdn.contentful.com/spaces/{SPACE}/graphql/alpha

Any client requesting content from the API needs to provide an access token in the Authorization header, specifically, Authorization: Bearer MY_TOKEN. The tokens are the same as those used in the Content Delivery API. You can find more in the documentation for the Content Delivery API.

Locale handling

You can only query content for one locale at a time. You must specify the locale in the URL query string using the locale parameter and the locale code. For example

https://cdn.contentful.com/spaces/{SPACE}/graphql/alpha?locale={LOCALE}

Unlike the CDA, the GraphQL API:

If no locale parameter is provided or the locale does not exist the API responds with a 401.

Schema generation

The GraphQL schema is automatically derived from the content model, and kept in sync whenever changes are made to its content types

Content types

The GraphQL schema is generated from the content types defined in your space master environment. For each content type in your master environment the GraphQL API will create a corresponding GraphQL type. The name of this type will be the pascalcase version of the content type after stripping it from any non alphanumeric character. For example:

"My 2Content-Type" --> "My2ContentType"

Let's suppose we have defined a single content type like the following:

{
  name: "Friendly User"
  sys: {
    ...
  },
  fields: [
    ...
  ]
}

Using this content type definition the API will automatically generate the corresponding schema. Notice how the GraphQL type is named after the content type name. The produced Query object will also expose two fields that you can use to query content of that type: one to fetch individual content documents (friendlyUser in the example) and another to do queries over all the content of the type (friendlyUserCollection). Collections are explained in more detail below.

type Sys {
  id: String
}

type FriendlyUser {
  sys: Sys
  fields: FriendlyUserFields
}

type FriendlyUserCollection {
  skip: Int!
  limit: Int!
  total: Int!
  nodes: [FriendlyUser]!
}

type Query {
  friendlyUser(id: String!): FriendlyUser
  friendlyUserCollection( skip: Int, limit: Int ): FriendlyUserCollection!
}

The schema generation might fail if:

  • Any content type name starts with a non-letter character
  • There are duplicated type names after transforming the content type names.

If this happens, you will need to modify the name of the offending content type(s) in order for the GraphQL API to work

Fields

A GraphQL type field gets created for each field in the corresponding content type. The name of the field will be the content type field id, and its type will be determined based of the following mappings:

Contenful type GraphQL type
Symbol String
Text String
Number Float
Integer Int
Date String
Boolean Boolean
Array of Symbol [String]
Array of Number [Float]
Array of Integer [Int]

Fields of type Link are handled as explained in Modelling relationships. The following Contentful types are yet not supported: Location and Object.

We will now continue with the previous example and extend our Friendly User content type with thre fields: age, name and adresses.

{
  name: "Friendly User"
  sys: {
    id: "friendlyUser",
    ...
  },
  fields: [
    {
      id: "age",
      type: "Integer"
    },
    {
      id: "name",
      type: "Symbol"
    },
    {
      id: "addresses",
      type: "Array",
      items: {
        type: "Symbol"
      }
    }
  ]
}

The resulting GraphQL schema will be:

type FriendlyUser {
  sys: Sys
  fields: FriendlyUserFields
}

type FriendlyUserFields {
  age: Int
  name: String
  addresses: [String]
}

Modelling relationships

One of the benefits of GraphQL is that it simplifies traversing the graph of relationships between different types.

In Contentful, relationships are modeled using links. An entry field can be a link to another entry or a list to other entries. The content type of the entries that can be linked from a given field can be restricted using the linkContentType validation. In the GraphQL schema we use this validation to determine the type of a link field.

A field may also link to an asset by specifying linkType: "Asset". In this case there is no need for a linkContentType validation, the GraphQL type of the field is always Asset.

If no linkContentType validation is provided for an entry link field we will not resolve the linked entries. The field will have type string in the GraphQL schema and the value will be an error message.

Below we explain in detail how different kinds of relationships can be modeled and how the corresponding GraphQL schema looks like.

One-to-one single-type relationships

One-to-one single-type relationships are modeled by content type fields that link to at most one entry of a fixed type. For example each FriendlyUser entry has a manager field that links to one entry of content type FiendlyUser.

{
  name: "Friendly User"
  sys: {
    id: "friendlyUser",
    ...
  },
  fields: [
    ...,
    {
      id: "manager",
      type: "Link",
      linkType: "Entry",
      validations: [{ linkContentType: ["friendlyUser"] }]
    }
  ]
}

This results in the following schema

type FriendlyUser {
  sys: Sys
  fields: FriendlyUserFields
}

type FriendlyUserFields {
  # ... other fields
  manager: FriendlyUser
}

One-to-one multi-type relationships

It is possible for an entry field to link to entries of different content types. For example each FriendlyUser may have a pet that is either a Dog or a Cat. This is modeled with the following content types.

{
  name: "Cat",
  sys: {
    id: "cat",
    ...
  }
}

{
  name: "Dog",
  sys: {
    id: "dog",
    ...
  }
}

{
  name: "Friendly User"
  sys: {
    id: "friendlyUser",
    ...
  },
  fields: [
    ...,
    {
      id: "pet",
      type: "Link",
      linkType: "Entry",
      validations: [{ linkContentType: ["cat", "dog"] }]
    }
  ]
}

This results in the following schema

type Dog {
  # ...
}

type Cat {
  # ...
}

type FriendlyUser {
  sys: Sys
  fields: FriendlyUserFields
}

union FriendlyUserPet = Cat | Dog

type FriendlyUserFields {
  age: Int
  name: String
  addresses: [String]
  pets: [FriendlyUserPetsItem]
  manager: FriendlyUser
  # ...
  pet: FriendlyUserPet
}

One-to-many single-type relationships

One-to-many relationships are modeled with links of arrays. For example a FriendlyUser might have multiple friends.

{
  name: "Friendly User"
  sys: {
    id: "friendlyUser",
    ...
  },
  fields: [
    ...,
    {
      id: "friends",
      type: "Array",
      items: {
        type: "Link",
        linkType: "Entry",
        validations: [{ linkContentType: ["friendlyUser"] }]
      }
    }
  ]
}

In the resulting GraphQL schema the friends field is of a collection type, the same type that is used for top-level collections for the FriendlyUser content type. The field has the same skip and limit arguments as the top-level collection field and the same limits apply.

type FriendlyUser {
  sys: Sys
  fields: FriendlyUserFields
}

type FriendlyUserFields {
  # ...
  friends(skip: Int, limit: Int): FriendlyUserCollection
}

type FriendlyUserCollection {
  skip: integer!
  limit: integer!
  total: integer!
  items: [FriendlyUser]!
}

One-to-many multi-type relationships

As with one-to-one relationships, a collection field may link to entries of different content types. For example a FriendlyUser might have multiple pets, each of which is either a Dog or a Cat.

{
  name: "Cat",
  sys: {
    id: "cat",
    ...
  }
}

{
  name: "Dog",
  sys: {
    id: "dog",
    ...
  }
}

{
  name: "Friendly User"
  sys: {
    id: "friendlyUser",
    ...
  },
  fields: [
    ...,
    {
      id: "pets",
      type: "Array",
      items: {
        type: "Link",
        linkType: "Entry",
        validations: [{ linkContentType: ["dog", "cat"] }]
      }
    }
  ]
}

This results in the following schema

type Dog {
  # ...
}

type Cat {
  # ...
}

type FriendlyUser {
  sys: Sys
  fields: FriendlyUserFields
}

union FriendlyUserPetsItem = Cat | Dog

type FriendlyUserPets {
  skip: Int!
  limit: Int!
  total: Int!
  items: [FriendlyUserPetsItem]!
}

type FriendlyUserFields {
  # ...
  pets(skip: Int, limit: Int): FriendlyUserPets
}

Assets

Assets in Contentful have a predefined schema. This means that the type for any asset in the GraphQL schema will follow the definition below:

type Asset {
  sys: Sys
  fields: AssetFields
}

type AssetFields {
  title: String
  file: AssetFile
}

type AssetFile {
  contentType: String
  fileName: String
  url: String
  details: AssetFileDetails
}

type AssetFileDetails {
  size: Int
  image: AssetFileDetailsImage
}

type AssetFileDetailsImage {
  width: Int
  height: Int
}

Filters on single resources

The following filters are available whenever querying a single resource:

  • id. You have to use the id filter to specify which entry you want to fetch.

    query {
      friendlyUser (id: "my-nice-user") {
        fields {
          name
          age
        }
      }
    }
    

Collections

Collections of entries are exposed through collection fields in the root query object and in one-to-many relationship fields. For example

type FriendlyUserCollection {
  skip: Int!
  limit: Int!
  total: Int!
  items: [FriendlyUser]!
}

type Query {
  # ...
  friendlyUserCollection(skip: Int, limit: Int): FriendlyUserCollection
}

A collection field has two optional arguments. skip denotes the zero-indexed offset in the collection from which items are fetched and limit the maximum number of items to fetch. By default skip is 0 and limit is 10. The maximum value for limit is 10. The value retuned from a collection field contains the meta fields skip, limit and, total and the requested items in the items field. The skip and limit fields corresponds to respective input arguments. The total fields contains the total number of items in that collection.

Exploring the schema with GraphiQL

You can explore and inspect the schema of a space using the GraphiQL, an in-browser GraphQL IDE. To open GraphiQL visit the https://cdn.contentful.com/spaces/{SPACE}/graphql/alpha/explore?locale={LOCALE}&access_token={CDA_TOKEN} URL in your browser. You must provide the CDA_TOKEN as a query parameter.