How to perform a nested query in graphql using gatsby-source-prismic - graphql

I'm just getting started with gatsby and graphql and I've started using prismic as a cms. I cannot figure out how to perform nested queries, avoiding overfetching. I don't even know if it would be possible or if it's more just me thinking of the problem in SQL terms.
I have two custom types on prismic which are related using a content relationship. These are Productions which have many People through a repeatable group of fields. The result I want is to have a page (the home) which shows the latest production with the list of people who starred in it, and another page with each person from people and all their roles in every production.
I managed to do this by fetching all the people in the home page and the required production and filtering the returned data in the frontend via javascript. However, I really feel that this way is not ideal since it requires to fetch all the people and not only the ones required for my page.
allPrismicProduction(
limit: 1
sort: { fields: [last_publication_date], order: DESC }
) {
edges {
node {
data {
casting {
...castingData
}
}
}
}
}
allPrismicPerson {
edges {
node {
id
data {
name {
text
}
photo {
url
}
}
}
}
}
}
const productions = data.allPrismicProduction.edges
const people = data.allPrismicPerson.edges
const sortedprods = []
productions.forEach(el => {
let castings = el.node.data.casting.map(cast => cast.person.uid)
castings.forEach(casting =>{
people.filter(person => {
if(castings.indexOf(person.node.uid) > -1){
return person
}
sortedprods.push({
production: el.node,
people: relpeople,
})
})
})
So what I do is I fetch all people and then filter them according to the uids found in the productions returned by the query.
I would like to know if it is possible, or otherwise what would be a better way to achieve this, how to limit overfetching by making it possible to only fetch the people who's uid is present in the productions given by the first part of the query.
Is this way of thinking compatible with graphql?

I managed to solve this by looking around a bit more in the other issues of gatsby-source-prismic on github.
The related-content node can be queried using the following structure:
{
allPrismicMyContentType {
edges {
node {
data {
my_relations {
relation {
document {
data {
where in data you can access all the properties of the type needed.
In this way one can do a single query to get all related content on prismic

Related

Github Graphql Filter issues which has multiple labels

This query returns those issues which has either label1 or label2. Basically, It returns all the issues of label1 and label2.
{
repository(owner: "another-user", name: "another-repo") {
issues(first: 100, filterBy: { labels: ["label1", "label2"}"] }) {
nodes {
title
body
bodyHTML
bodyText
number
labels(first: 100) {
nodes {
color
name
id
}
}
author {
url
avatarUrl
login
}
}
}
}
}
I was trying to find a specific issue which have both label1 and label2 labels. How can I execute AND operation?
I often see areas where "filtering" an endpoint falls short and the answer is always to use the search node. You can use the search API, but if you really like GraphQL there's a search endpoint there, too.
https://docs.github.com/en/graphql/reference/queries#search
query {
search(query="repo:another-user/another-repo+label:label1+label:label2) {
nodes {
//work with nodes as usual
__typename
... on Issue {
author {
}
createdAt
}
}
}
}
Search Query Syntax
I find the documentation a little lacking here because they do not explain the + syntax used in #rbennett485's query, but you can infer that it is the opposite of the - (exclusion) to mean it must include the search criteria (label/repo/etc).
Understanding the search syntax
GraphQL Inline Fragments
Because the search node returns a "Union" (SearchItemResultItem) type we need to be able to switch on the particular object returned.
This is where GraphQL Inline Fragments come into play. In my example I "switch" based on the __typename of the returned node. __typename is known as a "Meta Field" and will be available in any spec compliant GraphQL server (GraphQL: October 2021 Edition:4.4 Type Name Introspection). For more see Introspection.
It's not possible to do with the graphql API other than by returning all the issues with those labels then filtering client side (docs https://docs.github.com/en/graphql/reference/input-objects#issuefilters).
You can do it with the search API instead though https://docs.github.com/en/rest/reference/search#search-issues-and-pull-requests.
Haven't tested but I think something like this should do the job
q=label:label1+label:label2+repo:another-user/another-repo

Graphql Filter by query

So I'm trying to learn graphql I've been playing around with the ENS subgraph on the graph
I've figured out how to do simple filtering but when I try to write more complex filters they do not compile.
I'm trying to get the top 5 transactions for the each of the top 5 domains. (e.g for each domain I want the top 5 transactions)
{
#Sample Query to get the first 5 domains (not needed for question but used to validate results)
domains(first: 5) {
id
name
labelName
labelhash
}
#attempt to filter the transfer.domain.id by TOP 5 domains.id
transfers(where: { domain { id: domains(first: 5) { id } } }) {
id
domain {
id
}
blockNumber
transactionID
}
}
EDIT I'm going to attempt to simplify my request since I'm not sure nesting queries is possible. How can I filter an inner query by Id:
transfers(where: {domain.id: "0x9c0fc2519ae862cee27778e5c34714d6c7e3ca21ad572df47ad9f6fe530909bd"}) {
id
domain {
id
}
blockNumber
transactionID
}
NOTE: Domain.Id = does not compile how would I write a filtered query like that?
However, My filter doesn't compile syntactically. How can I write a query which filters by a child property?
You can query like this
query {
getPost(id: "0x1") {
title
text
datePublished
}
}
Got this from https://dgraph.io/docs/graphql/queries/search-filtering/

How do you perform operations on variables in GraphQL?

I am a traditional programmer new to GraphQL and I can't seem to find documentation on what I consider the basics, aka manipulating variables. Note: I am using GraphQL with Shopify(Admin API), through an app GraphiQL, so that my effect syntax and capabilities.
Here is a piece of hypo (& broken) code where I have two iterations of the same code block
<>that tries to add two variables
<>on that sum items in a list. The specific code in this lines are guesswork..
If anyone has suggestions on sources for example code or API docs beyond GraphQL site, I have been searching and nothing I have found addresses this type of functionality.
query fiveorTenOrders($n: Int = 5,$m: list =[5,5], $boo: Boolean = true) {
FiveOrds: orders(first: $n) {
edges #include(if: $boo) {
node {
...ordrecs
}
}
}
#<<<HERE and as basic arithmetic>>>
TenOrds: orders(first: ($n+$n) {
edges #skip(if: $boo) {
node {
...ordrecs
#<<<OR HERE ...as a sum of list>>>
TenOrds: orders(first: $m:SUM {
edges #skip(if: $boo) {
node {
...ordrecs
}
}
}
}
fragment ordrecs on Order {
id
name
createdAt
shippingAddress {
id
city
provinceCode
zip
}
}
GraphQL does not currently support this sort of functionality (and may never). Variables are used as-is with no way to apply arbitrary transformations to them. In your example, you would need to sum the values yourself on the client and then inject them into the query as a separate variable.

GraphQL queries for Gatsby - Contentful setup with a flexible content model

I have a gatsby site with the contentful plugin and graphql queries (setup is working).
[EDIT]
My gatsby setup pulls data dynamically using the pageCreate feature. And populates my template component, the root graphql query of which I've shared below. I can create multiple pages using the setup if the pages on contentful follow the structure given in the below query.
[/EDIT]
My question is about a limitation I seemed to have come across or just don't know enough grpahql to understand this yet.
My high level content model 'BasicPageLayout' consists of references to other content types through the field 'Section'. So, it's flexible in terms of which content types are contained in the 'BasicPageLayout' and the order in which they are added.
Root page query
export const pageQuery = graphql`
query basicPageQuery {
contentfulBasicPageLayout(pageName: {eq: "Home"}) {
heroSection {
parent {
id
}
...HeroFields
}
section1 {
parent {
id
}
...ContentText
}
section2 {
parent {
id
}
...ContentTextOverMedia
}
section3 {
parent {
id
}
...ContentTextAndImage
}
section4 {
parent {
id
}
...ContentText
}
}
}
The content type fragments all live in the respecitve UI components.
The above query and setup are working.
Now, I have "Home" Hard coded because I'm having trouble creating a flexible reusable query. I'm taking advantage of contentful's flexible nature when creating the models, but haven't found a way to create that flexibility in the graphql query for it.
What I do know:
Graphql query is resolved at run time, so everything that needs to be fetched should be in that query. It can't be 'dynamic'.
Issue: The 'Section' fields in the basicPageLayout can link to any content type. So we can mix and match the granular level content types. How do I add the content type fragment (like ContentTextAndImage vs ContentText) so it is appropriate for that section instance ('Section' field in the query)?
In other words
I'd like the root query to get 'Home' data which might have 4 sections, all of type - ContentTextOverMedia
as well as 'About ' data that might have also have 4 sections but with alternating types - ContentText and ContentTextAndImage
This is the goal because I want to create content (Pages) by mix-matching content types on contentful, without needing to update the code each time a new Page is created. Which is why Contentful is useful and was picked in the first place.
My ideas so far:
A. Run two queries, in series. One fetches the parent.id on each section and that holds the content type info. Second fetches the data using the appropriate fragment.
B. Fetch the JSON file of the basicPageLayouts content instance (such as 'Home') separately through Contentful API, and using that JSON file create the graphql string to be used in each instance (So, different layout for Home, About, and so on)
This needs more experimentation, not sure if it's viable, could also be more complex then it needs to be.
So, please share thoughts on the above paths that I'm exploring or another solution that I haven't considered using graphql or gatsby's features.
This is my first question on SO btw, I've spent some time on refining it and trying to follow the guidelines but please do give me feedback in comments so I can improve even if you don't have an answer to my question.
Thanks in advance.
If I understood correctly you want to create pages dynamically from the data coming from Contentful.
You can achieve this using the Gatsbyjs Node API specifically createPage.
In your gatsby-node.js file you can have something like this
const fs = require('fs-extra')
const path = require('path')
exports.createPages = ({graphql, boundActionCreators}) => {
const {createPage} = boundActionCreators
return new Promise((resolve, reject) => {
const landingPageTemplate = path.resolve('src/templates/landing-page.js')
resolve(
graphql(`
{
allContentfulBesicPageLayout {
edges {
node {
pageName
}
}
}
}
`).then((result) => {
if (result.errors) {
reject(result.errors)
}
result.data.allContentfulBesicPageLayout.edges.forEach((edge) => {
createPage ({
path: `${edge.node.pageName}`,
component: landingPageTemplate,
context: {
slug: edge.node.pageName // this will passed to each page gatsby create
}
})
})
return
})
)
})
}
Now in your src/templates/landing-page.js
import React, { Component } from 'react'
const LandingPage = ({data}) => {
return (<div>Add you html here</div>)
}
export const pageQuery = graphql`
query basicPageQuery($pageName: String!) {
contentfulBasicPageLayout(pageName: {eq: $pageName}) {
heroSection {
parent {
id
}
...HeroFields
}
section1 {
parent {
id
}
...ContentText
}
section2 {
parent {
id
}
...ContentTextOverMedia
}
section3 {
parent {
id
}
...ContentTextAndImage
}
section4 {
parent {
id
}
...ContentText
}
}
}
note the $pageName param that's what was passed to the component context when creating a page.
This way you will end up creating as many pages as you want.
Please note: the react part of the code was not tested but I hope you get the idea.
Update:
To have a flexible query you instead of having your content Types as single ref field, you can have one field called sections and you can add the section you want there in the order you desire.
Your query will look like this
export const pageQuery = graphql`
query basicPageQuery($pageName: String!) {
contentfulBasicPageLayout(pageName: {eq: $pageName}) {
sections {
... on ContentfulHeroFields {
internal {
type
}
}
}
}
Khaled

How can I do a WpGraphQL query with a where clause?

This works fine
query QryTopics {
topics {
nodes {
name
topicId
count
}
}
}
But I want a filtered result. I'm new to graphql but I see a param on this collection called 'where', after 'first', 'last', 'after' etc... How can I use that? Its type is 'RootTopicsTermArgs' which is likely something autogenerated from my schema. It has fields, one of which is 'childless' of Boolean. What I'm trying to do, is return only topics (a custom taxonomy in Wordpress) which have posts tagged with them. Basically it prevents me from doing this on the client.
data.data.topics.nodes.filter(n => n.count !== null)
Can anyone direct me to a good example of using where args with a collection? I have tried every permutation of syntax I could think of. Inlcuding
topics(where:childless:true)
topics(where: childless: 'true')
topics(where: new RootTopicsTermArgs())
etc...
Obviously those are all wrong.
If a custom taxonomy, such as Topics, is registered to "show_in_graphql" and is part of your Schema you can query using arguments like so:
query Topics {
topics(where: {childless: true}) {
edges {
node {
id
name
}
}
}
}
Additionally, you could use a static query combined with variables, like so:
query Topics($where:RootTopicsTermArgs!) {
topics(where:$where) {
edges {
node {
id
name
}
}
}
}
$variables = {
"where": {
"childless": true
}
};
One thing I would recommend is using a GraphiQL IDE, such as https://github.com/skevy/graphiql-app, which will help with validating your queries by providing hints as you type, and visual indicators of invalid queries.
You can see an example of using arguments to query terms here: https://playground.wpgraphql.com/#/connections-and-arguments

Resources