Gatsby custom Remark frontmatter variables not being passed into index.js - graphql

first question on StackOverflow!
Using the Gatsby blog template, I've modified the graphql query and verified that it returns the correct data in GraphiQL, which is being pulled from a "redirect:" property in the blog post frontmatter.
Unfortunately it isn't being passed in the data when running the index.js file.
gatsby-config.js
feeds: [
{
serialize: ({ query: { site, allMarkdownRemark } }) => {
return allMarkdownRemark.nodes.map(node => {
return Object.assign({}, node.frontmatter, {
description: node.excerpt,
redirect: node.frontmatter.redirect,
date: node.frontmatter.date,
url: site.siteMetadata.siteUrl + node.fields.slug,
guid: site.siteMetadata.siteUrl + node.fields.slug,
custom_elements: [{ "content:encoded": node.html }],
})
})
},
query: `
{
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date] },
) {
nodes {
excerpt
html
fields {
slug
}
frontmatter {
redirect
title
date
}
}
}
}
`,
output: "/rss.xml",
},
],
gatsby-node
type Frontmatter {
redirect: String
title: String
description: String
date: Date #dateformat
}
My code repository, https://github.com/tomvaillant/my_blog
Thanks for any support!

You need to query for the redirect field in the same way you do in the gatsby-config. Your query should look like:
export const pageQuery = graphql`
query {
site {
siteMetadata {
title
}
}
allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
nodes {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
redirect # <-- here
}
}
}
}
`

Related

Variable "$productSlug" is never used in operation "SingleProduct". Graphql error. Variables are not working in my Gatsby graphql queries

I am getting this error: Variable "$productSlug" is never used in operation "SingleProduct".
I also use gatsby-source-wordpress to query fields from wordpress to gatsby. I also uninstalled Gatsby and re-installed it to see if it works in different versions, but it didn't.
I searched all over the internet and stack overflow for an answer, I really believe it must be a bug with Gatsby or gatsby-source-wordpress,
this is the code:
const path = require("path");
const { createFilePath } = require(`gatsby-source-filesystem`);
exports.onCreatePage = ({ page, actions }) => {
const { createPage } = actions;
if (page.path.match(/products/)) {
page.context.layout = "ArchiveProduct";
createPage(page);
}
if (page.path.match(/products\/([^\/]+$)/)) {
page.context.layout = "SingleProduct";
createPage(page);
}
};
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions;
if (node.internal.type === `allWpProduct`) {
const slug = createFilePath({ node, getNode, basePath: `pages` });
createNodeField({
node,
name: `slug`,
value: slug,
});
}
};
exports.createPages = async function ({ graphql, actions }) {
const { data } = await graphql(`
query SingleProduct {
allWpProduct {
nodes {
uri
slug
id
}
}
}
`);
data.allWpProduct.nodes.forEach((node) => {
// const slug = node.slug;
actions.createPage({
path: "/products/" + node.slug,
component: path.resolve("./src/templates/SingleProduct.js"),
context: {
productSlug: node.slug,
productId: node.id,
layout: "SingleProduct",
},
});
});
};
And this is the query:
export const query = graphql`
query SingleProduct($productSlug: String!) {
wpProduct(slug: { eq: "$productSlug" }) {
title
slug
link
id
date(formatString: "MMMM DD, YYYY")
product {
description
price
slug
photo {
localFile {
childImageSharp {
gatsbyImageData
}
}
}
}
}
}
`;
Try the following:
export const query = graphql`
query SingleProduct($productSlug: String!) {
wpProduct(filter: {slug: { eq: "$productSlug" }}) {
title
slug
link
id
date(formatString: "MMMM DD, YYYY")
product {
description
price
slug
photo {
localFile {
childImageSharp {
gatsbyImageData
}
}
}
}
}
}
`;
Your issue appears because $productSlug is lifted properly via context but never used in any sort of filtering action inside the query.
I'd recommend you check it before in the GraphiQL playground hardcoding the $productSlug to check the output.

Nextjs - Contentful - Tags - Dynamic pages

I would like to create dynamic pages when I click a tag in an article or elsewhere on my website.
I'm using Next.js, SSG, and fetching the articles containing the tags from Contentful with the following GraphQL queries:
export async function getArticles() {
const articlesQuery = gql`
{
articleCollection(order: date_DESC) {
items {
title
slug
excerpt
date
contentfulMetadata {
tags {
name
id
}
}
featuredImage {
title
url
width
height
}
author {
name
photo {
fileName
url
width
height
}
title
twitterProfile
linkedInProfile
slug
}
}
}
}
`;
return graphQLClient.request(articlesQuery);
}
export async function getArticle(slug) {
const articleQuery = gql`
query getArticle($slug: String!) {
articleCollection(limit: 1, where: { slug: $slug }) {
items {
title
slug
excerpt
date
contentfulMetadata {
tags {
name
id
}
}
featuredImage {
title
url
width
height
}
author {
name
photo {
fileName
url
width
height
}
title
twitterProfile
linkedInProfile
slug
}
content {
json
links {
entries {
block {
sys {
id
}
__typename
... on VideoEmbed {
title
embedUrl
}
... on CodeBlock {
description
language
code
}
}
}
assets {
block {
sys {
id
}
url
title
width
height
}
}
}
}
}
}
}
`;
return graphQLClient.request(articleQuery, {
slug,
});
}
The contentfulMetadata is where the tags come from:
contentfulMetadata {
tags {
name
id
}
}
This is my [id].jsx file:
import { getArticles, getArticle } from "#utils/contentful";
export async function getStaticPaths() {
const data = await getArticles();
return {
paths: data.articleCollection.items.map((article) => ({
params: { id: article.contentfulMetadata.tags[0].id },
})),
fallback: false,
};
}
export async function getStaticProps(context) {
const data = await getArticle(context.params.id);
return {
props: { article: data.articleCollection.items[0] },
};
}
export default function TagPage({ article }) {
return (
<div>
<h1>{article.contentfulMetadata.tags.id}</h1>
</div>
);
}
I get the following error:
Error: Error serializing `.article` returned from `getStaticProps` in "/tags/[id]". Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
When console.log(data.articleCollection.items.contentfulMetadata.tags.id); or console.log(data.articleCollection.items.contentfulMetadata.tags[0].id); within getStaticPaths function it provides the following error:
TypeError: Cannot read property 'tags' of undefined
Can anyone show how to create a dynamic page ([id].jsx) file, which shows the tag id as the header <h1> as well as all articles containing the same tag?
Contentful DevRel here 👋🏼.
article.contentfulMetadata.tags is an array, as an entry can have more than one tag. So you'll need to access the tag you want via article.contentfulMetadata.tags[0].id or article.contentfulMetadata.tags[desired_index].id and so on.
Here's an example GraphQL query:
query {
blogPostCollection {
items {
contentfulMetadata {
tags {
id
name
}
}
}
}
}
And here's the response with tags as an array:
"data": {
"blogPostCollection": {
"items": [
{
"contentfulMetadata": {
"tags": [
{
"id": "salmastag",
"name": "Salma s tag"
}
]
}
},
{
"contentfulMetadata": {
"tags": []
}
}
]
}
}
}
Notice how if a blog post doesn't have any PUBLIC tags assigned (the second entry in the response), an empty array is returned — you might want to do some safety checking in your code for this.

Gatsby & Graphql: Filter allMarkdownRemark for pages matching a variable

I'm trying to filter all my markdown pages, for pages which match a certain category(specified in markdown frontmatter) and pages which are not the pages being currently visited. All pages created from markdown though are receiving the same result for allMarkdownRemark though and are not filtering any results.
I would like to know how to get all the pages to not have the same result for allMarkdownRemark, and would like the results in allMarkdownRemark to be filtered
My page query looks something like
export const pageQuery = graphql`
query ArticleQuery($path: String, $category: String, $title: String) {
allMarkdownRemark(
filter: {
frontmatter: {
category: {eq: $category},
title: {ne: $title}
}
},
sort: {
order: DESC, fields: [frontmatter___date]
}
) {
...
And my gatsby-node.js looks like
const { createFilePath } = require(`gatsby-source-filesystem`);
const { fmImagesToRelative } = require('gatsby-remark-relative-images-v2');
const path = require("path");
exports.createPages = async ({ actions: { createPage }, graphql }) => {
const postTemplate = path.resolve(`src/components/ArticlePage.jsx`);
const result = await graphql(`
{
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___title] }
) {
edges {
node {
fields {
slug
}
frontmatter {
category
title
}
}
}
}
}
`);
if (result.errors) {
return Promise.reject(result.errors);
};
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: `${node.fields.slug}`,
category: `${node.frontmatter.category}`,
title: `${node.frontmatter.title}`,
component: postTemplate,
context: {node}
});
});
};
exports.onCreateNode = ({ node, getNode, actions }) => {
fmImagesToRelative(node);
if (node.internal.type === `MarkdownRemark`){
actions.createNodeField({
node,
name: `slug`,
value: createFilePath({ node, getNode, basePath: `pages`, trailingSlash: true }),
});
}
};
All markdown files include something like this in them
---
title: "My title"
category: "My category"
---
I would like to know how to get all the pages to not have the same
result for allMarkdownRemark, and would like the results in
allMarkdownRemark to be filtered
In these cases, what is commonly used is a key field for all kind of markdown files that you want to group. As you said, allMarkdownRemark is a schema inferred by Gatsby (and their transformers and sharps) at the time that you allow it to access to your filesystem so you can't distinguish directly the type of markdown. This is the simplest, cleanest, and less invasive option. You just need to:
---
title: "My title"
category: "My category"
key: "pageTypeOne"
---
Then, in your queries, you just need to filter for key field when needed:
export const pageQuery = graphql`
query ArticleQuery($path: String, $category: String, $title: String) {
allMarkdownRemark(
filter: {
frontmatter: {
category: {eq: $category},
title: {ne: $title}
key: {eq: "pageTypeOne" }
}
},
sort: {
order: DESC, fields: [frontmatter___date]
}
) {
...
You can change the string-based approach to a context one if needed in your createPage API in your gatsby-node.js. Or, depending on your needs, create a filtered query in your gatsby-node.js, creating different queries for each page, in that way your markdownRemark will be filtered already.
Alternatively, you can add different filesystems (gatsby-source-filesystem) and use the inferred sourceInstanceName to get your data:
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/posts/`,
},
},
Then:
{
allFile(filter: { sourceInstanceName: { eq: "posts" } }) {
edges {
node {
extension
dir
modifiedTime
}
}
}
}

GraphQL filtering/sorting DatoCMS GatsbyJS

I have the following GraphQL query:
export const query = graphql`
query NewsQuery($slug: String!) {
datoCmsNews(slug: { eq: $slug }) {
id
title
description
slug
meta {
publishedAt
}
cover {
fluid(maxHeight: 530) {
...GatsbyDatoCmsSizes
}
}
}
allDatoCmsNews(sort: { fields: [meta___publishedAt], order: DESC }, limit: 4) {
edges {
node {
id
title
slug
meta {
publishedAt
isValid
status
}
cover {
fluid(maxHeight: 375) {
...GatsbyDatoCmsSizes
}
}
}
}
}
}
`;
On my allDatoCmsNews query how would I go about about sorting/filtering out a News item where a $slug is equal to the current slug? I don't want to show a News item if that news item is currently being viewed. I'm guessing I would have to use neq just struggling with the correct syntax.
Thanks
Pretty trivial using filter:
allDatoCmsNews(sort: { fields: [meta___publishedAt], order: DESC }, limit: 4, filter: {slug: {ne: $slug}})

How to work around GraphQL error, field "x" must not have a selection since type "String" has no subfields

I am using Gatsby and GraphQL, and I am new to GraphQL.
I have the following schema definition:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions;
const typeDefs = `
type MarkdownRemark implements Node {
frontmatter: Frontmatter
}
type Frontmatter {
title: String!
products: [Product]
}
type Product #dontInfer {
name: String!
price(price: Int = 1): Float
description: String
images: [ProductImage]
}
type ProductImage {
url: String
}
`;
createTypes(typeDefs);
};
Then on my page I use the following query:
query {
markdownRemark(fileRelativePath: { eq: "/content/pages/products.md" }) {
...TinaRemark
frontmatter {
title
products {
name
price
description
images {
url {
childImageSharp {
fluid(maxWidth: 1920) {
...GatsbyImageSharpFluid_withWebp
}
}
}
}
}
}
html
}
}
I then receive the following error:
Field "url" must not have a selection since type "String" has no subfields.
Does anyone have any suggestions on how to work around this error?
Also, what is childImageSharp? I'm wondering what the terminology is to define it. Is it a GraphQL "selector" or "function"?
It should be
query {
markdownRemark(fileRelativePath: { eq: "/content/pages/products.md" }) {
...TinaRemark
frontmatter {
title
products {
name
price
description
images {
url
}
}
}
html
}
}
Because you definition is
type ProductImage {
url: String
}
The url apparently has no sub fields.
For what it's worth (I don't know if this is related to your specific issue.) If your markdown path for the image file is invalid, GraphQL will return this error, interpreting the path as a string. I had this problem and it went away when I realized I had misspelled the path in the markdown.
productImage {
childImageSharp {
gatsbyImageData(width: 200)
}
}
I had a similar problem with returning a boolean. For me, instead of something like this
mutation {
someFunc(
memo: "test memo"
) {
success
}
}
I needed this
mutation {
someFunc(
memo: "test memo"
)
}

Resources