Gatsby & Graphql: Filter allMarkdownRemark for pages matching a variable - graphql

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

Related

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

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
}
}
}
}
`

Gatsby - runQuery inside createResolver using group

I would like to use createResolvers to run a query using group, but I don't think this is possible at the moment. According to Accessing Gatsby’s data store from field resolvers, it seems runQuery currently accepts filter and sort query arguments, but not group. I was hopeful that runQuery would support group (based on https://github.com/gatsbyjs/gatsby/issues/15453), but I can't find any examples of grouping using runQuery
If possible, how can I use createResolvers to run a query using group for the following graphql query?
query {
allFile(filter: { internal: { mediaType: { eq: "text/markdown" } } }) {
group(field: sourceInstanceName) {
fieldValue
totalCount
}
}
}
For context, I have 2 folders (coding & recipes) that I am sourcing markdown files from. Here is my gatsby-config.js:
const path = require('path')
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `coding`,
path: path.resolve(__dirname, `src/contents/coding`),
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `recipes`,
path: path.resolve(__dirname, `src/contents/recipes`),
},
},
{
resolve: `gatsby-transformer-remark`,
},
],
}
Currently, I am able to use runQuery to run queries using filter. Here is my gatsby-node.js:
exports.createResolvers = ({ createResolvers }) => {
createResolvers({
Query: {
recipes: {
type: `[File!]!`,
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
internal: { mediaType: { eq: 'text/markdown' } },
sourceInstanceName: { eq: 'recipes' },
},
},
type: `File`,
firstOnly: false,
})
},
},
coding: {
type: `[File!]!`,
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
internal: { mediaType: { eq: 'text/markdown' } },
sourceInstanceName: { eq: 'coding' },
},
},
type: `File`,
firstOnly: false,
})
},
},
},
})
}
But now I want to use createResolver to runQuery using group. Is this possible? If so, how?
I've produced a minimal repo at https://github.com/kimbaudi/gatsby-group-query
Currently, the home page (src/pages/index.jsx) is displaying the folders (coding & recipes) and the count of markdown files in that folder:
using the following page query:
export const query = graphql`
query {
allFile(filter: { internal: { mediaType: { eq: "text/markdown" } } }) {
group(field: sourceInstanceName) {
fieldValue
totalCount
}
}
}
`
and I would like to create a resolver that groups and use it in place of the above page query.

How to add variables to graphQL query?

I am using Gatsby.
I need to increase the limit of 3 to each onCLick.
I've tried to follow this post, but with no success, so I removed the edits, and here is the original code...
This will help me to load more posts.
export const LatestNews = ({data}) => {
console.log(data);
return (
<section id="news_posts_section">
<p>some data</p>
</section>
);
};
export const latestNewsQuery = graphql`
query myquery{
allMarkdownRemark(
filter: { frontmatter: { layout: { eq: "news" } } }
sort: { fields: frontmatter___date, order: DESC }
limit: 2
) {
nodes {
frontmatter {
layout
title
path
date
featuredImage
thumbnail
}
excerpt(pruneLength: 325)
}
}
}
`;
Here is my gatsby-node:
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
const blogPostTemplate = path.resolve('src/templates/blog-post/BlogPost.js');
const newsTemplate = path.resolve('src/templates/news-single/NewsSingle.js');
const latestNewsPage = path.resolve(
'src/components/pages-implementation/news/sections/LatestNews.js',
);
const blog = graphql(`
{
blog: allMarkdownRemark(
filter: { frontmatter: { layout: { eq: "blog" } } }
) {
edges {
node {
frontmatter {
path
}
}
}
}
}
`).then((result) => {
if (result.errors) {
result.errors.forEach((e) => console.error(e.toString()));
return Promise.reject(result.errors);
}
const posts = result.data.blog.edges;
posts.forEach((edge) => {
const { path } = edge.node.frontmatter;
createPage({
path: path,
component: blogPostTemplate,
context: {},
});
});
});
const news = graphql(`
{
news: allMarkdownRemark(
filter: { frontmatter: { layout: { eq: "news" } } }
) {
edges {
node {
frontmatter {
path
}
}
}
}
}
`).then((result) => {
if (result.errors) {
result.errors.forEach((e) => console.error(e.toString()));
return Promise.reject(result.errors);
}
const news = result.data.news.edges;
news.forEach((edge) => {
const { path } = edge.node.frontmatter;
createPage({
path: path,
component: newsTemplate,
context: {},
});
});
news.forEach(edge => {
createPage({
path: `/news/`,
component: latestNewsPage,
context: {
// This time the entire product is passed down as context
product: edge
}
});
});
});
return Promise.all([blog, news]);
};
Edit 21 November:
I replaced the above code with my attempt to use a non-static query
I added the gatsby-node config
I give here a little explanation: the BlogPost.js and NewsSingle.js are templates that create new pages for each post or news post (from Netlify CMS)
The LatestNews.js is a component in a page. This is the main page of the news. Where are shown all the news? With a static query, it works fine, however, I need to make the "limit" a variable in order to apply a load more button, that onClick will increase the limit, thus showing more news posts on the loop.
With the above configuration it says:
warning The GraphQL query in the non-page component "/home/user/projectname/src/components/pages-implementation/news/sections/LatestNews.js" will not be run.
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
useStaticQuery (hence the name) does not allow to receive variables. If you take a look at the docs:
useStaticQuery does not accept variables (hence the name “static”),
but can be used in any component, including pages
The only way to pass variables in a GraphQL query is by using the context API in the gatsby-node.js. For example:
queryResults.data.allProducts.nodes.forEach(node => {
createPage({
path: `/products/${node.id}`,
component: productTemplate,
context: {
// This time the entire product is passed down as context
product: node
}
});
});
};
In the snippet above, will be a product variable in the context with the whole node. It can be accessed through pageContext prop in the destination template or used as a query parameter.

How to do a nested mutation resolver with nexus-prisma

I have the following datamodel:
type Job {
// ...
example: String
selections: [Selection!]
// ...
}
type Selection {
...
question: String
...
}
I define my object type so:
export const Job = prismaObjectType({
name: 'Job',
definition(t) {
t.prismaFields([
// ...
'example',
{
name: 'selections',
},
// ...
])
},
})
I do my resolver this way:
t.field('createJob', {
type: 'Job',
args: {
// ...
example: stringArg(),
selections: stringArg(),
// ...
},
resolve: (parent, {
example,
selections
}, ctx) => {
// The resolver where I do a ctx.prisma.createJob and connect/create with example
},
})
So now in the resolver I can receive the selections as json string and then parse it and connect/create with the job.
The mutation would look like this:
mutation {
createJob(
example: "bla"
selections: "ESCAPED JSON HERE"
){
id
}
}
I was wondering if there's anything more elegant where I could do something like:
mutation {
createJob(
example: "bla"
selections: {
question: "bla"
}
){
id
}
}
or
mutation {
createJob(
example: "bla"
selections(data: {
// ...
})
){
id
}
}
I've noticed that with nexus-prisma you can do stringArg({list: true}) but you can't really do objects.
My main question is what is the most elegant way to do either nested mutation or connect all in one.
You can use an inputObjectType as shown in the docs:
export const SomeFieldInput = inputObjectType({
name: "SomeFieldInput",
definition(t) {
t.string("name", { required: true });
t.int("priority");
},
});
Make sure to include the type as part of the types you pass to makeSchema. You can then use it to define an argument, like
args: {
input: arg({
type: "SomeFieldInput", // name should match the name you provided
}),
}
Now, the argument value will be available to your resolver as a regular JavaScript object, not a String. If you need a list of input objects, or want to make the argument required, you do so using the same options you would provide with when using a scalar -- list, nullable, description, etc.
Here's a complete example:
const Query = queryType({
definition(t) {
t.field('someField', {
type: 'String',
nullable: true,
args: {
input: arg({
type: "SomeFieldInput", // name should match the name you provided
}),
},
resolve: (parent, { input }) => {
return `You entered: ${input && input.name}`
},
})
},
})
const SomeFieldInput = inputObjectType({
name: "SomeFieldInput",
definition(t) {
t.string("name", { required: true });
},
});
const schema = makeSchema({
types: {Query, SomeFieldInput},
outputs: {
...
},
});
Then query it like:
query {
someField(
input: {
name: "Foo"
}
)
}
Or using variables:
query($input: SomeFieldInput) {
someField(input: $input)
}

Is it possible to use dynamic query alias names in GraphQL?

I am currently working on a Gatsby documentation site. One particular page retrieves the content of various README files for HTML/CSS components which are grouped into three different categories, based on regex searches of their local filepath structure. I'm using 3 separate aliased queries at the moment to retrieve very similar data and the DRY coder in me feels this should be possible with one and a $group-type variable (which would replace atoms, molecules and organisms in the below code) or something similar. As I'm a real newbie to GraphQL I'm not sure if this is possible and I can't seem to find anyone doing this online. Here's what I have so far:
export const pageQuery = graphql`
query($path: String!) {
pageData:
markdownRemark(fields: { slug: { eq: $path } }) {
html
fields {
slug
title
}
fileAbsolutePath
}
atoms:
allMarkdownRemark(sort: {order: ASC, fields: [fields___title]}, limit: 1000, filter: {fileAbsolutePath: {regex: "/dl-atoms/"}}) {
edges {
node {
fields {
slug
title
}
}
}
}
molecules:
allMarkdownRemark(sort: {order: ASC, fields: [fields___title]}, limit: 1000, filter: {fileAbsolutePath: {regex: "/dl-molecules/"}}) {
edges {
node {
fields {
slug
title
}
}
}
}
organisms:
allMarkdownRemark(sort: {order: ASC, fields: [fields___title]}, limit: 1000, filter: {fileAbsolutePath: {regex: "/dl-organisms/"}}) {
edges {
node {
fields {
slug
title
}
}
}
}
}
`;
You can define fragments to use in your queries. These allow you to define a selection set once and then use it just by referencing the name of the fragment. Do note that you have to know the name of the type for which you're specifying the selection set.
export const pageQuery = graphql`
query($path: String!) {
pageData:
markdownRemark(fields: { slug: { eq: $path } }) {
html
fields {
slug
title
}
fileAbsolutePath
}
atoms:
allMarkdownRemark(sort: {order: ASC, fields: [fields___title]}, limit: 1000, filter: {fileAbsolutePath: {regex: "/dl-atoms/"}}) {
...MarkdownRemarkFields
}
molecules:
allMarkdownRemark(sort: {order: ASC, fields: [fields___title]}, limit: 1000, filter: {fileAbsolutePath: {regex: "/dl-molecules/"}}) {
...MarkdownRemarkFields
}
organisms:
allMarkdownRemark(sort: {order: ASC, fields: [fields___title]}, limit: 1000, filter: {fileAbsolutePath: {regex: "/dl-organisms/"}}) {
...MarkdownRemarkFields
}
}
fragment MarkdownRemarkFields on MarkdownRemarkConnection {
edges {
node {
fields {
slug
title
}
}
}
}
`;
Fragments are mentioned in the Gatsby docs here.

Resources