Optional queries in Gatsby / GraphQL - graphql

I have a Gatsby site with a contentful source, the initial page build GraphQL looks like this:
{
allContentfulProduct {
edges {
node {
slug
contentfulid
}
}
}
}
this works fine when using the preview API, but when using the production one it fails unless I have at least 1 Product entry published:
There was an error in your GraphQL query:
Cannot query field "allContentfulProduct" on type "Query". Did you mean ... [suggested entry names] ?
I'm pretty sure that when I publish a Product things will work as expected, but is there any way to make this query optional. The query should return zero results, and thus no Product pages will be created (expected outcome if no Product entries are published)

Try to add into your gatsby-node.js file and play around that.
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type allContentfulProduct implements Node{
slug: String,
contentfulid: String
}`

Related

Gatsby - Cannot query field on type Error when field in CMS is empty

I found out that this is known problem with Strapi + Gatsby setup, but I found some articles about how to get rid of that error like the following ones:
https://www.virtualbadge.io/blog-articles/nullable-relational-fields-strapi-gatsbyjs-graphql
https://medium.com/swlh/gatsby-graphql-and-the-missing-but-necessary-explanation-about-type-definitions-87a5ef83e759
And indeed it helped with the bug itself, but caused another. At the moment when I fill the field inside the CMS graphql cannot see it and interprets it as null.
Without gatsby-node.ts:
And With gatsby-node.ts:
import type { GatsbyNode } from 'gatsby'
export const sourceNodes: GatsbyNode['sourceNodes'] = async ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type STRAPI__COMPONENT_BASE_HERO implements Node {
backgroundVideo: STRAPI__MEDIA
}
`
createTypes(typeDefs)
}

Issues with Gatsby & Strapi optional data

I have a model made in Strapi which contains a specific component which can be used to add social media links. Each link contains a text field and a link field. Everything works as expected, except when I leave it empty. If there are 0 links I get an error which is shown below.
This is how the component looks inside Strapi:
Gatsby GraphQL trying to access the links:
strapiWebsiteSetting {
footerSocialLinks {
text
link
}
footerOtherLinks {
text
link
}
}
The error I get when there is 0 links added:
Is there a way of making GraphQL work even if there is 0 links added. I've tried
adding the following code to gatsby-node.js but that didn't work:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions;
const typeDefs = `
type STRAPI__COMPONENT_LINK_FOOTER_OTHER_LINK implements Node {
id: ID!
parent: Node
children: [Node!]!
internal: Internal!
text: String
link: String
strapi_id: Int
}
type STRAPI__COMPONENT_LINK_FOOTER_SOCIAL_MEDIA_LINK implements Node {
id: ID!
parent: Node
children: [Node!]!
internal: Internal!
text: String
link: String
strapi_id: Int
}
`;
createTypes(typeDefs);
};
A less painful way to make gatsby and strapi work with optional attributes and even optional records is the following:
Install the gatsby-plugin-schema-snapshot plugin.
Set the "update" option to "true" in gatsby-config.js (could also be handled by environment variable):
{
resolve: `gatsby-plugin-schema-snapshot`,
options: {
update: true,
},
}
Build your app with all content-types having at least one record (e.g. a dummy record) and all attributes present. That creates a schema.gql file in the root of your app folder.
Set the plugin's "update" option to "false" and remove your dummy record.
Build again. No complaints anymore :)
So, I managed to get it working. Creating the custom types in the gatsby-node.js probably did the trick, but I haven't really tested it, I just know it works.

Confused why returnPartialData works without a field policy in Apollo Client 3

In my application I am searching for products, then clicking into a product to see more detail about it.
I perform a GraphQL query on each page. The SEARCH query returns type [Product], and the PRODUCT query returns type Product.
// Search page
const SEARCH = gql`
query Search($query: String!) {
searchResults: search(query: $query) {
id
name
images
price
}
}
`
// ProductDetail page
const PRODUCT = gql`
query Product($id: Int!) {
product(id: $id) {
id
name
images
optionSetName
options {
id
images
name
}
price
}
}
`
I have enabled returnPartialData on the PRODUCT query, as some of the fields for that product already exist in the cache from the SEARCH query, and I would like to access them before the server request returns.
I thought I would also have to apply a field policy to reference the pre-existing Product, as I don't know how PRODUCT even knows what its return type is.
However, when I do the following:
const { loading, data: { product } = {} } = useQuery(
PRODUCT,
{ variables: { id: productId, isShallow }, returnPartialData: true }
)
console.log(product)
the following is logged to console (the first is from returnPartialData, the second from server):
Somehow the PRODUCT query has associated itself with the existing Product, without me explicitly writing a cache redirect.
I'm confused how this has occurred? It seems like Apollo must have a reference to the GraphQL schema, and has seen the return type of PRODUCT is Product, then automatically used the id arg to reference the existing product.
Using "#apollo/client": "^3.4.1"
Wow, turns out I had made a field policy ages ago and forgotten about it... xD
typePolicies: {
Query: {
fields: {
product: {
read (_, { args, toReference }) {
return toReference({
__typename: 'Product',
id: args.id
})
}
}
}
}
}

Gatsby Remark plugin: Created nodes visible to GraphIQL but queries on Gatsby pages return null

I'm working on a plugin for gatsby-transformer-remark. In my plugin, I take data from the Remark transformer and create a graphql node of a new type. When I start the site and open GraphIQL at http://localhost:8000/___graphql, I can see my plugin's new node and its data. However, when I try to access that node's data in a Gatsby page component, the graphql query returns null for that node type. Why does the data not appear in the graphql query on my page? Can I not create new graphql nodes in Remark plugins? The Remark gatsby plugin API provides the necessary methods for creating nodes.
I have a minimal reproduction repo at this link. Instructions for running it and seeing the issue are in the readme.
https://github.com/timothymcmackin/test-create-nodes
I create the graphQL node in plugins/gatsby-remark-internal-toc/index.js by using the createNode method:
if (headings.length > 0) {
const { createNode } = actions;
const headingNode = {
headings: headings,
path: markdownNode.frontmatter.path,
id: createNodeId(`${markdownNode.frontmatter.path}.${markdownNode.frontmatter.title}`),
children: [],
internal: {
description: `Headings and links for ${markdownNode.fileAbsolutePath}`,
type: "topicInternalHeadings",
contentDigest: createContentDigest(headings),
content: JSON.stringify(headings),
}
};
await createNode(headingNode);
}
I start the site with the Gatsby develop command. Then I open graphIQL and run this query:
query MyQuery {
topicInternalHeadings(path: {eq: "/mypage.html"}) {
headings {
level
path
text
}
}
}
The query returns data for the topicInternalHeadings node.
Then I open http://localhost:8000/myPage.html which has a similar query on it. However, the results of its query are:
{markdownRemark: {…}, topicInternalHeadings: null}
Expected result
The graphql node data that I create in the plugin is available to both graphiql and graphql queries on Gatsby pages.
Actual result
The node data is available to graphiql but graphql queries on Gatsby pages return null.
In my case it was that the query in my page had no name, so instead of this:
query {
allMdx(sort: {fields: frontmatter___date, order: DESC}, limit: 6) {
edges {
.....
Do this:
query SomeMeaningFulName {
allMdx(sort: {fields: frontmatter___date, order: DESC}, limit: 6) {
edges {

GatsbyJs - How to handle empty graphql node from Contentful plugin

I am building a site in Gatsbyjs that pulls information in via the gatsby-source-contentful plugin, but I'm struggling with the graphql side of things.
If I have a Content model in Contentful that contains a field to override the default description for example - then if none of the content uses that yet graphql throws an error if I try to include it in my query.
Is there anyway to short circuit the graphql queries?
Examples
{
allContentfulPage {
edges {
node {
title
description {
description
}
}
}
}
}
This will break if there is no Page model that exists with a description, but as soon as one page gets a description it works.
Gatsby pulls data from Contentful and then it builds an internal "model" of what the data from Contenful looks like. The Contentful API is REST but internally Gatsby uses GraphQL.
If a field does not have a value on Contentful then it will not be a part of the generated GraphQL query in Gatsby. The solution is to push a single value in one of your records.
The other solution would be to create and define the ContentfulPage type in your gatsby-node.js file. here is the link to gatsby documentation if you need to read more .
To create types you could add this to your gatsby-node.js file:
const createSchemaCustomization = ({ actions }) => {
const types = [
`
type ContentfulPage implements Node {
description : ContentfullDescription
}
type ContentfullDescription implements Node {
description : String
}
`,
];
actions.createTypes(types);
};
const gatsbyNode = {
createSchemaCustomization,
};
export default gatsbyNode;
this is only the case if the description is a string, if it is some other type such as rich text you should add its type the createTypes api. then it would be:
const createSchemaCustomization = ({ actions }) => {
const types = [
`
type ContentfulPage implements Node {
description : ContentfullDescription
}
type ContentfullDescription implements Node {
description : ContentfulRichText
}
type ContentfulRichText {
raw: String
references: [Node] #link(from: "references___NODE")
}
`,
];
actions.createTypes(types);
};
const gatsbyNode = {
createSchemaCustomization,
};
export default gatsbyNode;
Please take note that I am just guessing the type of description it might be some other types that you need to create yourself.

Resources