For the past year, DevMynd engineers have been shifting our web and mobile development stack to React.js/native backed by a GraphQL endpoint written in Ruby on Rails. We use the Relay framework for our components to manage communication with the graph. This has worked surprisingly well in a wide variety of projects. However, our pattern of using Pundit or CanCanCan for authorization of user requests had to be updated in our Rails app to reflect the new single-endpoint nature of the web server.
The GraphQL Relay Specification is actually pretty simple. There is an interface Node
that has a globally unique ID. Graph types
can implement that interface. If they do, Relay can query the Graph for that Node by ID without traversing the graph.
The issue we’re trying to solve is for a public graph that contains data the user must be authorized to access. The graphql-ruby gem provides helpers for implementing the Relay interface. We also need our top-level Viewer
field to complete the root of the graph. Our Query root ends up looking something like this:
With this graph definition, all authorization can be done at the node level. As you traverse the graph, the resolve
code blocks can check the current user’s authorization before yielding sensitive data. For example, on an open source project I use CanCanCan and populate the graph context with both a current user and ability in the GraphqlController.
I can then use the ability to populate the graph. For reference, here is the ViewerType on the project. Note the resolve block filters the list of objects based on the ability:
Now, the rest of the graph is properly using the authorization ruleset to expose the correct data to each user. The last step is using the ability for a Node by ID. This was a bit trickier. The gem helper for the node field does take in a resolve block but you need to take care that the block is resolved to the helper. Here is the authorized QueryType root:
Now the node
and nodes
fields both take the current ability into account when resolving the object.