shiftx/faunadb-graphql-lib

Support Relay connections spec

Closed this issue · 5 comments

EDIT: I mistakenly assumed that Fauna's GraphQL pagination fields had a 1:1 correspondence with FQL's Pagination function arguments, and my original comments were based off of my experience with Fauna's GraphQL pagination. I see now that Pagination supports specifying a cursor by ref, which is very similar to how Relay connection cursors work.


Many GraphQL clients and servers support Relay-style connections as a solution for pagination:

It would be really great if faunadb-graphql-lib also supported these connections, especially since Fauna's own GraphQL schema doesn't support them.

FQL's Paginate and Relay connections offer cursor-based pagination. The biggest difference is in how cursors are used:

  • In Relay connections, the cursor encodes an item's position within an array. You pass the cursor to after and provide how many items you want with first for forward pagination (e.g., after: ..., first: 10 gets the first 10 results after ...) or you use before and last for backward pagination (e.g., before: ..., last: 10 gets the last 10 results after ...).
  • In FQL Paginate, the cursor encodes a position before or after an item, which implies directionality. You pass the cursor as the cursor parameter and the quantity you want to the size parameter (e.g., Paginate(input, {cursor: ..., size: 10}) gets the first or last 10 items before or after ..., and the direction depends on the cursor itself).

Given the differences between the two approaches, any implementation would have to create its own cursors rather than rely on those provided by Fauna.

I think that a good place to start for generating cursors might be a base64 encoded version of the ID:

Especially if the cursors are opaque, either offset or ID-based pagination can be implemented using cursor-based pagination (by making the cursor the offset or the ID), and using cursors gives additional flexibility if the pagination model changes in the future. As a reminder that the cursors are opaque and that their format should not be relied upon, we suggest base64 encoding them.

However, I don't know whether this is too naive. For example, how unique are Fauna ID's? Does the documentation for the NewId function apply to all ID's?

This NewId function produces a unique number. This number is guaranteed to be unique across the entire cluster and once generated is never generated a second time.

It may actually be possible to use connectionFromPromisedArraySlice from graphql-relay to implement connections without any further work to the AST transform. I will investigate.

Since faunas cursors are based on the shape of the underlying index (so could be an array with ref(s), strings, numbers etc.) the current approach has worked well as a generic solution for us.

Yes, looks like you're right. The queries get translated to an on-the-wire JSON representation by faunadb-js that represents the shape of the cursor ref. GraphQLFaunaCursorType is able to serialize and deserialize this JSON representation.

I did see in the Fauna Slack that the team hasn't committed to any particular wire protocol for transmitting queries back to Fauna, so it's possible that the underlying JSON structure will change in the future. If they move to a binary protocol, then the current approach in GraphQLFaunaCursorType might not work, depending on the driver library's implementation. But let's not worry about that 🙂.

Anyway, I wanted to say that I was able to implement Relay connections using this library. My general approach is:

  1. Modify astToFaunaQuery to allow leaf nodes to access fql queries. (See issue #3.)
  2. For each connection, create a custom connection and edge class using GraphQLFaunaCursorType for the cursor field.
  3. Create a generic PageInfo class using GraphQLFaunaCursorType for the startCursor and endCursor fields.
  4. Create FQL queries in the fql properties of each relevant field to get the cursors, etc.

I'm currently using this approach in my fork. I'll submit a PR for issue #3 and possibly create a generic solution for Relay connections once this project goes officially public. Closing this issue for now.