Was this page helpful?

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.
  • 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:

  • Page size for collections is subject to a limit of 10. See the Collections section for details.
  • There is only a limited set of filters currently supported. For more information see the GraphQL filter reference.

Endpoint URL and authentication

The Contentful GraphQL API is available at

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

It is also available for specific environments at

https://cdn.contentful.com/spaces/{SPACE}/environments/{ENVIRONMENT}/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 specify a locale in the URL query string using the locale query parameter:

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

If you don't specify a locale, the default locale of the space is used.

Unlike the CDA, the GraphQL API does not support the locale wildcard. You can only query content for one locale at a time.

If requested locale does not exist the API responds with a 400.

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 the specified environment of the space. (Or master if no environment has been specified.) For each content type in your environment the GraphQL API will create a corresponding GraphQL type. The name of this type will be the pascalcase version of the content type ID 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:

{
  sys: {
    id: "friendly-user"
  },
  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 ID. 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
  spaceId: String
  environmentId: String
}

type FriendlyUser {
  sys: Sys
  # ... fields
}

input FriendlyUserFilter {
  # ... field based filters
}

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

type Query {
  friendlyUser(id: String!): FriendlyUser
  friendlyUserCollection(
    skip: Int
    limit: Int,
    where: FriendlyUserFilter
  ): 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 DateTime
Boolean Boolean
Object String
Array of Symbol [String]
Array of Number [Float]
Array of Integer [Int]

Fields of type Link are handled as explained in Modelling relationships.

Fields of type Object are returned as a string containing the serialized JSON data of the field.

Fields of type Location are handled as explained in Locations.

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

{
  name: "Friendly User"
  sys: {
    id: "friendly-user",
    ...
  },
  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
  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. It will work without, but for a better experience we encourage to always define linkContentType for your link fields. 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.

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 FriendlyUser.

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

This results in the following schema

type FriendlyUser {
  sys: Sys
  manager: FriendlyUser
  # ... other fields
}

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: "friendly-user",
    ...
  },
  fields: [
    ...,
    {
      id: "pet",
      type: "Link",
      linkType: "Entry",
      validations: [{ linkContentType: ["cat", "dog"] }]
    }
  ]
}

This results in the following schema

type Dog {
  # ...
}

type Cat {
  # ...
}

union FriendlyUserPet = Cat | Dog

type FriendlyUser {
  sys: Sys
  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: "friendly-user",
    ...
  },
  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
  friends(skip: Int, limit: Int): FriendlyUserCollection
  # ...
}

type FriendlyUserCollection {
  skip: Int!
  limit: Int!
  total: Int!
  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: "friendly-user",
    ...
  },
  fields: [
    ...,
    {
      id: "pets",
      type: "Array",
      items: {
        type: "Link",
        linkType: "Entry",
        validations: [{ linkContentType: ["dog", "cat"] }]
      }
    }
  ]
}

This results in the following schema

type Dog {
  # ...
}

type Cat {
  # ...
}

union FriendlyUserPetsItem = Cat | Dog

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

type FriendlyUser {
  sys: Sys
  pets(skip: Int, limit: Int): FriendlyUserPets
  # ...
}

Inline fragments

Since every content type implements the same common interface of Entry, the content type of the entries can be linked without validation. That also means that no content type can be named as Entry.

  type Entry {
     sys: Sys
  }

Link to single entry

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

{
  name: "Friendly User"
  sys: {
    id: "friendly-user",
    ...
  },
  fields: [
    ...,
    {
      id: "manager",
      type: "Link",
      linkType: "Entry",
    }
  ]
}

This results in the following schema

type FriendlyUser implements Entry {
  sys: Sys
  manager {
    ... on FriendlyUser {
      # some user fields
    }
  }
  # ... other fields
}

The relationships are modeled by content type fields that link to a collection of entities. For example each FriendlyUser entry has a managers field that links to collection of entries of content type FriendlyUser.

{
  name: "Friendly User"
  sys: {
    id: "friendly-user",
    ...
  },
  fields: [
    ...,
    {
      id: "manager",
      type: "Array",
      items: {
        type: 'Link',
        linkType: 'Entry'
      }
    }
  ]
}

This results in the following schema

type FriendlyUser implements Entry {
  sys: Sys
  manager {
    items {
      ... on FriendlyUser {
        # some user fields
      }
    }
  }
  # ... other fields
}

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
  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
}

Assets are also supported as root queries. Currently there are two root queries for it: single asset and collection of assets.

type Query {
  # ...
  asset (id: String!): Asset
  assetCollection(skip: Int, limit: Int): AssetCollection
}

The queries above returns following graphql types:

type Asset {
  sys: Sys
  title: String
  file: AssetFile
}

type AssetCollection {
  skip: Int!
  limit: Int!
  total: Int!
  items: [Asset]!
}

Locations

Locations are represented as types with the properties lat and lon. Here's what the GraphQL schema looks like

type Location {
  lat: Float
  lon: Float
}

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") {
        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]!
}

input FriendlyUserFilter {
  # ... field based filters
}

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

Arguments

A collection field has three optional arguments:

  • skip denotes the zero-indexed offset in the collection from which items are fetched. The default is 0.
  • limit denotes the maximum number of items to fetch. The default is 10 and the maximum is 10.
  • where contains the filter specifications to apply on the collection query. For more information see the GraphQL filter reference

Return value

The value returned 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?access_token={CDA_TOKEN} URL in your browser. You must provide the CDA_TOKEN as a query parameter.

GraphQL Errors

The GraphQL API responses will contain the errors which happened during the different phases of a request (authentication, validation, schema generation and execution). Among these errors there can also be other internal system errors.

The errors returned by our API follow the GraphQL error spec. We add an contentful object to the extensions error property to add information relevant to the failed request to facilitate debugging and fixing problems. The contentful object contains the following properties:

  • code: unique error identifier
  • requestId: unique request identifier which
  • details: optional object with details about a specific kind of error

Following is an example of such a response from the API

{
  data: { // query data: optional, might be presented in case of partial response queries
    ...
  },
  errors: [{
    message: 'Query execution error. Query too complex to be executed in allocated resources', // Human readable error message
    locations: [{line: 4, column: 17}],
    path: ['too', 'many', 'db_ops'],
    extensions: {
      contentful: {
        code: 'TOO_COMPLEX_QUERY', // text error code
        requestId: 'xxx' // id of current request
      }
    }
  }]
}

List of known errors:

Category Message Is partial
Authentication ACCESS_TOKEN_MISSING no
Authentication ACCESS_TOKEN_INVALID no
Schema generation INVALID_TYPE_NAME no
Schema generation COLLIDING_TYPE_NAMES no
Schema generation RESERVED_TYPE_NAME no
Schema generation RESERVED_FIELD_NAME no
Schema generation LINKED_CONTENT_TYPES_DO_NOT_EXIST no
Validation UNKNOWN_ENVIRONMENT no
Validation UNKNOWN_LOCALE no
Query execution TOO_COMPLEX_QUERY yes
Query execution UNRESOLVABLE_LINK yes
Query execution LINK_WITHOUT_TYPE_VALIDATIONS no
System errors INTERNAL_SERVER_ERROR no

*Is partial indicates whether for given error partial data response is possible.