GraphQL Content Relationship Queries for Multiple Items

If I'm understanding this document correctly, Prismic does not allow querying of content relationships by UID. And the limitation looks even more severe - you can only get one document (by ID) back at a time?

You must use the document ID

When querying by a Content relationship field, the value passed to the argument can only be the ID of a given document. If you use a different value, for example: a UID field, the query won't work.

In my example, I'd like to get n News articles from South Carolina (I don't know how many there will be):

  allNewss( where: { locations : "south-carolina" }) {
    edges {
      node {
        title
        _meta {
          id
          uid
          type
          lang
        }
      }
    }
  }

What's the workaround here? How do I get a variable number of my news items from your endpoint?

What type is the south-carolina field?

You could use tags, you then query like this:

query {
  allNews (tags: "south-carolina") {
    edges {
      node {
        title
        _meta {
          id
          uid
          type
          lang
        }
      }
    }
  }
}

You can query multiple tags too - see the docs:

Yeah but the tags are pages themselves, I need content on those. They arent just organizational to the system.

south-carolina is a location in Prismic.

PROBLEM:

  • I need a tagging content-type for my site based on physical location, call it locations. My spec requires each location have it's own page along with being a tag on other content-types. This rules out the Prismic tag system, because it cannot be used as a content-type or generate pages strangely.

  • The recommendation from Prismic if you need true tags is to create a custom content-type as a tag. Once that is set up, you can model a group in another content-type to create a multi-select of content-relationships. This allows you to tag between content-types now (ie, 'I want to tag a news item with three locations').

  • As noted in this thread and in the Prismic docs, if you're using GraphQL you are out of luck. You can't look inside a group with aGraphQL union for whatever reason. So, to recap, you can build a multi-tag set up on the admin side but you just can't even query for it.

Obviously, this sucks. It seems like a huge oversight for a CMS but we're all developers here and are used to encountering weird circumstances. So let's get into a solution.

SOLUTION

If you keep digging in the Prismic docs, you will find that you can query for a group of content relationships inside of content-type...but it only works in REST for whatever reason. So to actually fetch tags articles within a content-type, you need to use both GraphQL and then do a callback with id of the tag INTO a REST query. That's a mouthful - let's break it down since Prismic doesn't seem to want to.

CODE

Let's assume you're on an individual instance of a tag page (/locations/[uid].js) and want to display 3 news articles tagged with that location. Getting the location is pretty easy in GraphQL - take note of the id inside of _meta - you will need that in a second.

query LocationByUID($uid: String!, $lang: String!) {
    locations(uid: $uid, lang: $lang) {
      header_image
      location
      intro
      _meta {
        uid
        id
        type
      }
   }
}

You want to take that response and store it into a response variable - like:
const locationResults = [ data ]

The part of the response you need for the REST call would be locations._meta.id in my example, because content-relationship queries require using the id, not the uid - again this isn't explained, go take it up with Prismic.

The REST query to get news items by the location is pretty weird looking, very atypical at least to me. Pay attention to the Prismic.Predicates.at('my.news.locations.location', data.locations._meta.id) line, which effectively means 'look for news items tagged by the location id from the last query` - effectively a GraphQL callback:

  await Prismic.getApi(REF_API_URL).then(
    function(api) {  
      return api.query(
        [
          Prismic.Predicates.at('document.type', 'news'),
          Prismic.Predicates.at('my.news.locations.location', data.locations._meta.id),
        ],
        { orderings : '[my.news.publication_date desc]' }
      );
    }).then(
    function(resNews) {
      locationResults.push(resNews.results)
    }
  );

Taken altogether, you can now get a single location tag page with n number of news items tagged by location (using a multi-select tagging system):

export async function getSingleLocationsPage(uid, previewData) {
  const data = await fetchAPI(`
  query LocationByUID($uid: String!, $lang: String!) {
    locations(uid: $uid, lang: $lang) {
      header_image
      location
      intro
      _meta {
        uid
        id
        type
      }
    }
  }
  `,
    {
      previewData,
      variables: {
        uid,
        lang: API_LOCALE,
      },
    }
  )

  // Due to limitations with the GraphQL endpoint (can't query tags in groups)
  // We need to do a second call to get linked news, topics and grants per location
  // Build array for single location data
  const locationResults = [ data ]

  await Prismic.getApi(REF_API_URL).then(
    function(api) {  
      return api.query(
        [
          Prismic.Predicates.at('document.type', 'news'),
          Prismic.Predicates.at('my.news.locations.location', data.locations._meta.id),
        ],
        { orderings : '[my.news.publication_date desc]' }
      );
    }).then(
    function(resNews) {
      locationResults.push(resNews.results)
    }
  );

  return locationResults
}

This works pretty well in my testing but obviously feel hacky. Good luck coding peeps.

1 Like

Prismic GraphQL is still in (stable) beta at the moment, which is why not everything is available yet.

@phil, maybe worth a note to the development team?

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Re-opened for @mandy

Be careful @mandy, that's a trap if you're using it as a tagging system. You can't query for multiple tags the way you're doing it - you could only apply one tag in a where GraphQL query. The inability to query groups means you can't make a tagging setup and use GraphQL with Prismic. Wasted a whole day figuring this out.

Yes, it should be clarified that the solution I mentioned is if you only want to assign one linked document. So in the case where only one location, category, etc. will be allowed. I wanted to mention this since it could save someone quite a bit of trouble if they don't need to have more than one type of linked document assigned, and therefore have to deal with the limitations of groups.

This issue has been closed due to inactivity. Flag to reopen.

Thanks for your solution mixing GraphQL + REST @john1 !
I think what I want to achieve is related to this post.

Basically I'm trying to query inverse relations or reverse relationships, in particular for one-to-many relationships so that I can query references in both directions.

For example: I have:

  • post model: with link to products in a group field
  • product model

I'd like to query all the posts with the products associated with each post, but also return for each product a list of all the posts that reference it.

Will the API provide in the near future a way to get that data in one query? Or maybe this is already possible (with GraphQuery? GraphQL? REST?)

For now I have a GraphQuery:

const getPostsQuery = `{
  post {
    ...postFields
    post_products {
      products {
        ...on product {
          name
          // Here I'd like to have in the product returned data something like:
          // posts: [{post1...}, {post2...}] -> all the posts that reference the product
          // And sometimes I'd like to have only the number of times the product has been referenced, like:
          // postCount: 2 -> which means 2 posts has the product in their relation
        }
      }
    }
  }
}`

export async function getPosts() {
  const posts = await Client().query(
    Prismic.Predicates.at("document.type", "post"),
    {
      graphQuery: getPostsQuery,
    }
  );

  return posts;
}

I'm trying to build on top of Prismic but if those queries are not supported I'd sadly have to look for another CMS :confused:

I don't know if this is what you mentionned @john1 but I was trying to:

const productsPosts = await Client().query([
    Prismic.Predicates.at("document.type", "post"),
    Prismic.Predicates.at("my.post.products.product", productIds), // productIds = array of product ids
  ]);

And got an Error: Unexpected status code [400].

If instead I do:

const productsPosts = await Client().query([
    Prismic.Predicates.at("document.type", "post"),
    Prismic.Predicates.at("my.post.products.product", "YBpFzd5MAltX4"),
  ]);

this works. So yeah I can't query all the posts that reference a list of products.
@Phil can you confirm?

If that's the case, the only option would be to first get all the posts, then all the products, then calculate everything myself on server (which is not maintainable when having hundreds of posts and products). Besides we lose pagination.

In the worst case where I do everything on server, if I have 300 posts, can I query all 300 in one go or I have to make multiple requests to get all of them?

@john1 Did you find another way to do this or you had to implement this with another CMS?

Being Answered here:

@tommy.zrce - my solution above still stands as the only way I've gotten this to work.

@Phil - any updates on bringing this functionality to GraphQL?

Actually yes there's been progress, the functionality to query by a relationship in a group has been developed and we're are slowly rolling it out.

We can't activate it on a per repo basis for the moment, but once we can we'll update everyone in the following thread:

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.