2019-12-15
|~5 min read
|988 words
For a long time, my site has been fueled by a single graphql query. The site is a single page that is a running list of all my blog posts.
The more I write, the more unwieldy the site becomes. While adding search helped, I’ve got plans to add pagination and other pages in the future.
Before that could become a reality, I needed a way to pull back different data in different contexts.
That is, in order to create a page for each blog entry, I needed to retrieve the actual blog entry. However, to create a list of 10 blog entries, I needed the title and a slug.
The problem was that they were coming from the same place and without aliasing, that leads to errors. Fortunately, aliasing the query provided a solution.
Let’s take a look.
To begin, I had this query in my gatsby-node
.
query BlogPosts {
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
filter: { fields: { isPublished: { eq: true } } }
limit: 1000
) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
}
The results of this query are a series of edges, like so:
{
"data": {
"allMarkdownRemark": {
"edges": [
{
"node": {
"fields": {
"slug": "/2019-11-27/running-shell-scripts/"
},
"frontmatter": {
"title": "Shell Scripts: How To Run Them"
}
}
},
{
"node": {
"fields": {
"slug": "/2019-11-25/psql-tuples-only/"
},
"frontmatter": {
"title": "Postgres Tuples Only"
}
}
}
// ...
]
}
}
}
These were then passed into a createPage
action which used my blogPost
template to generate a post for each edge.
The Gatsby tutorial on pagination was my starting point for learning about pagination on Gatsby.
The result is a query that looks nearly identical to retrieving all posts, but includes a limit (i.e. the maximum number of results to return), and a skip (i.e. how many of the results to skip initially).
query PaginatedBlogPosts($skip: Int!, $limit: Int!) {
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: $limit
skip: $skip
) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
}
Okay, so I now have my paginated results, but I still need all of the results in order to create the pages for the blog posts.
Unfortunately, if I go ahead and and put these queries together, things break:
query CombinedQuery {
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
filter: { fields: { isPublished: { eq: true } } }
limit: 3
) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
allMarkdownRemark(
filter: { fields: { isPublished: { eq: true } } }
sort: { fields: [frontmatter___date], order: DESC }
limit: $limit
skip: $skip
) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
}
Fortunately, tools like GraphiQL will show you something’s afoot.
Fields “allMarkdownRemark” conflict because they have different arguments. use different aliases on the fields to fetch both if this was intentional.
If I proceed without addressing this issue, I’ll get an error in response:
{
"errors": [
{
"message": "Fields \"allMarkdownRemark\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.",
"locations": [
{
"line": 2,
"column": 3
},
{
"line": 14,
"column": 3
}
],
"stack": [
"GraphQLError: Fields \"allMarkdownRemark\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.",
" at Object.SelectionSet (/Users/stephen/_coding/personal/blog/node_modules/graphql/validation/rules/OverlappingFieldsCanBeMerged.js:71:29)",
" at Object.enter (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:324:29)",
" at Object.enter (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:375:25)",
" at visit (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:242:26)",
" at validate (/Users/stephen/_coding/personal/blog/node_modules/graphql/validation/validate.js:73:24)",
" at /Users/stephen/_coding/personal/blog/node_modules/express-graphql/index.js:121:32",
" at processTicksAndRejections (internal/process/task_queues.js:93:5)"
]
},
{
"message": "Variable \"$limit\" is not defined by operation \"BlogPosts\".",
"locations": [
{
"line": 14,
"column": 124
},
{
"line": 1,
"column": 1
}
],
"stack": [
"GraphQLError: Variable \"$limit\" is not defined by operation \"BlogPosts\".",
" at Object.leave (/Users/stephen/_coding/personal/blog/node_modules/graphql/validation/rules/NoUndefinedVariables.js:38:33)",
" at Object.leave (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:345:29)",
" at Object.leave (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:395:21)",
" at visit (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:242:26)",
" at validate (/Users/stephen/_coding/personal/blog/node_modules/graphql/validation/validate.js:73:24)",
" at /Users/stephen/_coding/personal/blog/node_modules/express-graphql/index.js:121:32",
" at processTicksAndRejections (internal/process/task_queues.js:93:5)"
]
},
{
"message": "Variable \"$skip\" is not defined by operation \"BlogPosts\".",
"locations": [
{
"line": 14,
"column": 138
},
{
"line": 1,
"column": 1
}
],
"stack": [
"GraphQLError: Variable \"$skip\" is not defined by operation \"BlogPosts\".",
" at Object.leave (/Users/stephen/_coding/personal/blog/node_modules/graphql/validation/rules/NoUndefinedVariables.js:38:33)",
" at Object.leave (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:345:29)",
" at Object.leave (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:395:21)",
" at visit (/Users/stephen/_coding/personal/blog/node_modules/graphql/language/visitor.js:242:26)",
" at validate (/Users/stephen/_coding/personal/blog/node_modules/graphql/validation/validate.js:73:24)",
" at /Users/stephen/_coding/personal/blog/node_modules/express-graphql/index.js:121:32",
" at processTicksAndRejections (internal/process/task_queues.js:93:5)"
]
}
]
}
So, if both queries are needed, but running them together creates errors, what is the solution? Aliases!
query combinedQuery($skip: Int!, $limit: Int!) {
paginatedResults: allMarkdownRemark(
filter: { fields: { isPublished: { eq: true } } }
sort: { fields: [frontmatter___date], order: DESC }
limit: $limit
skip: $skip
) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
publishedResults: allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
filter: { fields: { isPublished: { eq: true } } }
) {
edges {
node {
fields {
slug
isPublished
}
frontmatter {
title
date
publish
}
}
}
}
}
In this case, the first query of allMarkdownRemark
is aliased to paginatedResults
and the second to publishedResults
.
{
"data": {
"paginatedResults": {
"edges": [
{
// ...
}
// ...
]
},
"publishedResults": {
"edges": [
{
// ...
}
// ...
]
}
}
}
Once aliased, it’s important to remember that the data is no longer accessible through data.allMarkdownRemark
, but data.paginatedResults
and data.publishedResults
.
If you need data from the same field in two different ways, GraphQL will yell… unless you provide an alias. And since they’re simple to use, might as well!
Learning GraphQL - one bite at a time!
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!