2020-01-30
|~4 min read
|688 words
Sometimes, the easiest things are hard. Or they are until you learn how the syntax.
That’s what it felt like when I wanted to sort a GraphQL query on multiple fields.
In my case, I was working on organizing my books page where I store notes on books I’ve read.
I wanted to sort on title and author to account for situations where I’ve read multiple books by the same author.1
I write my book notes in markdown and I include several things in the frontmatter including the book’s title and the last name of the author. For example, a note might begin:
---
title: "Notes on David Copperfield by Charles Dickens"
bookTitle: "David Copperfield"
author: "Charles Dickens"
authorLast: "Dickens"
---
The body of the post goes here...
Initially, my page query was only concerned with getting the right posts from the file system using the custom field sourceInstance
:
export const pageQuery = graphql`
query {
books: allMarkdownRemark(
filter: { fields: { sourceInstance: { eq: "books" } } }
) {
/* ... */
}
}
`
What to do? I had a sort method on other pages, so I knew how to get started, but when I tried to add a second parameter, I was at a loss. My first (naïve) attempt:
export const pageQuery = graphql`
query {
books: allMarkdownRemark(
filter: { fields: { sourceInstance: { eq: "books" } } }
sort: { order: ASC, fields: [frontmatter___author] }
sort: { order: ASC, fields: [frontmatter___bookTitle] }
) {
/* ... */
}
}
`
This didn’t work and I received the error message: "message": "There can be only one argument named \"sort\".",
Okay, fair enough. A little searching later and I found the answer was staring me in the face: the array.
Arrays guarantee order and can store multiple values. This is exactly what I was looking for! I just hadn’t put the pieces together yet.
export const pageQuery = graphql`
query {
books: allMarkdownRemark(
filter: { fields: { sourceInstance: { eq: "books" } } }
sort: { order: [ASC, DESC], fields: [frontmatter___author, frontmatter___bookTitle] }
) {
/* ... */
}
}
`
Et Voilá! In the above example, GraphQl will sort the returned books by author (ascending) and book title (descending).
A few final notes on sorting with GraphQL:
If you’re only sorting on one parameter, the array is optional.
export const pageQuery = graphql`
query {
books: allMarkdownRemark(
sort: { fields: frontmatter___author }
) {
/* ... */
}
}
`
Sort order is optional. If no sort order is provided, GraphQL will default to ascending (ASC
).
To build on the above. This is true for all sort orders. So you can provide only the first one if you have two sort parameters for example. You cannot skip however. For example:
query {
books: allMarkdownRemark(
filter: { fields: { sourceInstance: { eq: "books" } } },
sort: { order: [, DESC], fields: [ frontmatter___date, frontmatter___bookTitle,] }
) {
/* ... */
}
}
You might expect this to order date
ascending and bookTitle
descending - however, in practice, I found that date
received the DESC
and bookTitle
fellback to the default ascending
. While I did test this, I did not verify it with the docs. YMMV.
The really cool part about this is that I can delete my old custom sorting function that I’d created as a starting point when I didn’t know how to sort with GraphQL
Sorting with GraphQL is simpler than I feared and intuitive once I understood the syntax! Things to remember:
Even better, now that I know how to do it, I can remove the custom sorting function that I’d created with Javascript that was already too complicated and didn’t even have multi-field sorting!
Good luck and good sorting!
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!