Client-Supplied Custom Sorting Using GraphQL
Most databases allow to sort on multiple fields at the same time, and the sorting order can differ from one to another, one way to translate this into GraphQL, and that I’ve been using, is to use enums and object input arguments.
Example
Let’s say we have a resolver getUsers: [User!]!
which returns users from MongoDB, and we are sorting the results by name
createdAt
:const users = await ctx.db.users.find().sort({ name: 1, // ascending createdAt: -1 // descending});
But we want to allow the client to specify the fields that are going to be sorted, and their respective order.
First, we could have a Direction
enum, which is going to have values ASC
and DESC
enum DirectionEnum { ASC DESC}
Using graphql-js
this could be defined like the following:
import { GraphQLEnumType } from 'graphql';
export const DirectionEnumType = new GraphQLEnumType({ name: 'DirectionEnum', values: { ASC: { value: 1, }, DESC: { value: -1, }, },});
Now let’s create a enum to represent the fields that are allowed to be sorted on our User
enum UserSortFieldEnum { NAME CREATED_AT}
In graphql-js
:
import { GraphQLEnumType } from 'graphql';
export const UserSortFieldEnumType = new GraphQLEnumType({ name: 'UserSortFieldEnum', values: { NAME: { value: 'name', }, CREATED_AT: { value: 'createdAt', }, },});
Now, to connect those two together, we are going to make a input type called UserSort
:
input UserSort { field: UserSortFieldEnum! direction: DirectionEnum!}
graphql-js
:
import { GraphQLInputObjectType, GraphQLNonNull } from 'graphql';import { DirectionEnumType } from './DirectionEnum';import { UserSortFieldEnumType } from './UserSortFieldEnum';
const UserSortInputType = new GraphQLInputObjectType({ name: 'UserSort', fields: () => ({ field: { type: GraphQLNonNull(UserSortFieldEnumType), }, direction: { type: GraphQLNonNull(DirectionEnumType), }, }),});
Then our getUsers
resolver will become:
getUsers(sort: [UserSort!]): [User!]!
And now the client can query it and specify any sort order they need:
query GetUsers { getUsers(sort: [ { field: NAME, direction: DESC, }, { field: CREATED_AT, direction: ASC, } ]) { # ... }}
And if you are using MongoDB, and are going to start using this, @entria/graphql-mongo-helpers
has a really simple helper to transform the sort
argument supplied by the client to something that can be used on MongoDB sort()
method.
It’s called buildSortArg
, using it on our getUser
resolver would look like this:
// ...import { buildSortFromArg } from '@entria/graphql-mongo-helpers';
// ... inside getUser resolver:const users = await ctx.db.users.find().sort(buildSortFromArg(args.sort));
The library also exports a SDL definition and GraphQLObject for the Direction enum, DirectionEnum
and DirectionEnumType
respectively.