Three new Contentful GraphQL features to make your life easier

Stefan Judis - Dec 4 '20 - - Dev Community

At Contentful, we believe that the use of API-first products is a significant evolution in how developers build digital experiences. We’ve seen, used and doubled down on RESTful APIs ourselves but, over the last two years, we discovered that it’s time to change the way developers query data. Serverless architectures and GraphQL as the transportation layer are the future. That’s why we made our GraphQL API available to all pricing tiers. And yes, that includes the free Community edition!

We continuously improve our GraphQL API, and the querying of localized and deeply nested content just got more powerful. In case you’re not following our changelog or haven’t subscribed to our developer newsletter, I want to share the latest GraphQL additions in this post.

Ready? Let’s dive in!

Two new localization arguments

Contentful allows you to create and handle content in different locales. These locales were reflected in our GraphQL API using the locale argument on single entity and collection fields. 

query {
  # query a specific post in the locale en-US
  post(id: "...", locale: "en-US") {
    Title # "Hello Fast Forward"
  }
  # query all posts in the locale en-US
  postCollection(locale: "en-US") {
    items {
      Title # "Hello Fast Forward"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

You can define which locale you want to query on a GraphQL entity level. All nested entity fields follow the locale that is defined higher in the query. If you don’t specify the locale argument, the API follows the space-specific default locale.

Query the same entry field in multiple locales

Previously, it was only possible to set the desired locale on a field entity level. If you wanted to access a specific entry field in multiple locales, you had to include the same entry multiple times in your GraphQL query.

query {
  # query English posts
  posts: postCollection(locale: "en-US") {
    items {
      Title # "Hello Fast Forward"
    }
  }
  # query Romanian posts
  postsRU: postCollection(locale: "ro") {
    items {
      Title # "Buna Fast Forward"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Thanks to the added locale argument on leaf fields, you can now clean up, remove the duplicated queries and include fields of multiple locales in one resource query!

query {
  posts: postCollection(locale: "en-US") {
    items {
      title # "Hello Fast Forward
      titleRO: title(locale: "ro") # "Buna Fast Forward
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Search for entry links in multiple/different languages

Speaking of localization, let’s take a look at a blog example. Assuming you’re running a magazine site that includes articles in multiple languages. 

You could define a content model to include one article type with many localized fields. These fields could represent the article title, description and body text in different languages. They would most notably also include a localized author field that references other authors depending on the article’s language. 

If you wanted to query all blog posts written in a language by one particular author, you could use the linkedFrom field and write a query like the following:

query {
  # query all authors with the "ro" locale
  authorCollection(locale: "ro") {
    items {
      name

      # query entries that link to this author
      linkedFrom {
        postCollection {
          items {
            title
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This query works great to receive entries in a single locale, but this approach falls short when you have a multilingual author that has written articles in different languages. 

To query all incoming links from multiple locales, you had to replicate the query and define different locale arguments on the entity level. While this works, it still isn’t great. 

We solved this issue and made the querying of localized reference content easier by introducing a new allowedLocales argument.

query {
  # query all authors with the "ro" locale
  authorCollection(locale: "ro") {
    items {
      name

      # query links to this author from different locales
      allIncomingLinks: linkedFrom(allowedLocales: ["ro", "en-US"]) {
        postCollection {
          items {
            title
          }
        }
      }

      # query links to this author from a specific locale
      englishIncomingLinks: linkedFrom(allowedLocales: "en-US") {
        postCollection {
          items {
            title
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

allowedLocales accepts one or multiple locales for the linkedFrom field to write more specific localized queries.

Advanced reference filtering using _exists

One of Contentful’s strengths is that it allows you to create reusable data structures. You define the content types that your application needs with just a few button clicks. 

Let’s take the same example. A magazine site could include articles and authors next to landing pages that include hero images and video elements. Contentful’s content structures are flexible, so it’s up to you to tailor everything to your needs and what you’re building. 

Members of our Slack Community pointed out the use case of querying for entries that include empty reference fields. For the blog example, it should be possible to query all entries that are not referencing any author. 

We thought that this was a great idea and extended the _exists filter possibilities. Previously, you could use _exists filters only for leaf fields. You could, for example, query for articles that don’t include a title the filter title_exists is available. Now you can use _exists for reference fields, too.

query {
  # query all posts that have a defined author reference
  postsWithAuthor: postCollection(where: {
    author_exists: true
  }) {
    items {
      title
    }
  }
  # query all posts that have no defined author reference
  postsWithoutAuthor: postCollection(where: {
    author_exists: false
  }) {
    items {
      title
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The _exists filter is available for single and multi-reference fields and is now at your service to find entries that are missing essential reference links.

More GraphQL features to come

These three additions were only the latest changes to our GraphQL API. If you have additional feedback, please don’t hesitate to provide it in our community channels. We’re listening and keen to learn more about your GraphQL needs.

Stay safe, and see you soon!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .