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