1. 程式人生 > >Client-Supplied Custom Sorting Using GraphQL

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

and 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.