Pretty schema-masking in GraphQL-Ruby
In this post, we’ll learn how to extend the GraphQL-Ruby gem to implement a nice API for schema masking.
Schema masking is a term used to describe the act of hiding certain elements from a schema depending on a runtime condition.
For example, depending on certain feature flags in your application you may want to expose a slightly different schema. A user with different permissions should maybe not be allowed to see certain fields or types.
Schema masking was introduced as a feature in graphql-ruby last year, and looks a bit like this:
As you can see, this is very simple and with a small schema, everything is fine. What happens when you’ve got a large app with multiple beta features and permission logic? This filter is going to grow huge:
OK, this is a bit ugly on purpose. But you see how this could get out of hand! In the official docs http://graphql-ruby.org/schema/limiting_visibility.html there’s an example of using member metadata to achieve something that looks a lot cleaner. What if we took that to another level and added some dynamism to that approach? For that, we’ll have to see how we can extend schema member definitions with custom metadata. Here’s a primer on how to do this:
Extending Schema Members
Each schema member class in GraphQL-Ruby responds to the
accepts_definition is a way for us to extend the gem’s DSL with our own metadata.
For example, if we wanted to add the a
topsecret metadata field on all `GraphQL::Field` objects like in the documentation example, it would be as simple as calling this:
We’re ready for the next step. In the real world, a simple
topsecret metadata field is not going to cut it. Each field’s visibility might depend on multiple things, from the query context to even globals in your application. What if members of your schema could dynamically define if it should be visible? In reality, the code is not even more complicated. Instead of accepting a
true | false value for
topsecret, we’re going to accept any object that responds to
call with a
If you wanted that option on all schema members instead of just on fields. Add the same
accepts_definitions call to the rest of the classes:
- Type (
- Argument (
- Enum value (
Now we get to define the visibility on a per member basis! Looks like this:
Putting it all together
The only thing left to do is adding our
only: filter from the original example. But this time it looks way simpler:
What we’re doing here is instead of having the logic for every member in the filter, we’re delegating it to the member, which now has a `visibility_proc` since we extended the DSL!
I hope you enjoyed the post. Schema masking is a powerful feature and thanks to the gem’s extendability we can make it work for us even better!
For more stuff on GraphQL feel free to follow me on Twitter: https://twitter.com/__xuorig__
Or checkout my website: http://mgiroux.me