Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GraphQL: How to implement pagination with graphQL-java?

Currently, I see no existing support for pagination in the graphql-java library. It does have some basic relay support, where-in, we can create a connection, Facebook's recommended way of implementing pagination.
This is the method which helps achieve that. However, with no documentation I'm finding it hard to understand how this function works. Can someone break-down the steps they would take to add pagination support if they already have an existing model which allows basic queries like Add, delete, fetch etc. using the graphql-java library?

like image 591
Scrotch Avatar asked Jun 15 '17 18:06

Scrotch


People also ask

Does GraphQL support paging?

To resolve this issue, GraphQL servers can paginate their list fields. This diagram shows offset-based pagination, in which a client requests a page based on an absolute index in the list ( offset ) and the maximum number of elements to return ( limit ).

How do you use limits in GraphQL?

GraphQL Limit and Offset In GraphQL, you can limit the number of rows returned by the query with the limit argument. limit takes an integer, representing the number of rows to return. In this example, you fetch only 5 todos.

What is Relay pagination?

Relay-style pagination is an opinionated flavor of cursor-based pagination for GraphQL APIs. Relay itself is a JavaScript framework that can be used as a client to retrieve and cache data from a GraphQL API.


1 Answers

You don't even need Relay connections to support pagination. Your query could simply accept a page number and size (or limit/offset) as arguments and return a list - done. But, if you wanted Relay connection for e.g. Book type, you'd do something like the following:

Relay relay = new Relay();
GraphQLOutputType book = ...; //build your normal Book object type
GraphQLObjectType bookEdge = relay.edgeType(book.getName(), book, null, Collections.emptyList());
GraphQLObjectType bookConnection = relay.connectionType(book.getName(), bookEdge, Collections.emptyList());

As a result, you'd have a BookConnection type that conforms to the Relay connection spec.

As for the example with basic GraphQL, you have a simple web app here.

The connection spec naturally fits a data store that supports cursor based pagination, but needs some creativity when used with different pagination styles.

1) If you wish to use simple offset based paging, you can decide to treat after as the offset (meaning a number would be passed), and first as the limit:

SELECT * FROM ORDER BY timestamp OFFSET $after LIMIT $first

The same for before and last, just different direction.

2) Another way is to treat after/before as the last seen value of the sort column (so an actual (obfuscated) value would be passed):

SELECT * FROM ORDER BY timestamp WHERE timestamp > $after LIMIT $first

I'd also recommend you take a look at my project, graphql-spqr, with an example app, that makes developing GraphQL APIs dead simple.

For example, you'd create a paginated result like this:

public class BookService {
    @GraphQLQuery(name = "books")
    //make sure the argument names and types match the Relay spec
    public Page<Book> getBooks(@GraphQLArgument(name = "first") int first, @GraphQLArgument(name = "after") String after) {
        //if you decide to fetch from a SQL DB, you need the limit and offset instead of a cursor
        //so, you can treat "first" as count as "after" as offset
        int offset = Integer.valueOf(after);
        List<Book> books = getBooksFromDB(first, offset);
        Page<Book> bookPage = PageFactory.createOffsetBasedPage(books, totalBookCount, offset);
        return bookPage;
    }
}

There's many other ways to create a Page instance, this is just the most straight-forward one.

You'd then generate a schema from your Java class:

GraphQLSchema schema = new GraphQLSchemaGenerator()
       .withOperationsFromSingleton(new BookService())
       .generate();
GraphQL graphQL = GraphQLRuntime.newGraphQL(schema).build();

And execute a query:

ExecutionResult result = graphQL.execute("{books(first:10, after:\"20\") {" +
                "   pageInfo {" +
                "       hasNextPage" +
                "   }," +
                "   edges {" +
                "       cursor, node {" +
                "           title" +
                "}}}}");

But, again, if you are not using Relay there's really no need to overcomplicate things. If your storage supports cursor-based pagination naturally, go for it. If it doesn't, just use the simple limit/offset arguments and return a list, and forget the connection spec. It was created to enable Relay to automatically manage paging in various scenarios, so it's almost always a total overkill if you're not using Relay and/or a DB with cursor-based pagination.

like image 192
kaqqao Avatar answered Nov 15 '22 21:11

kaqqao