With GraphQL-Ruby, it’s possible to hide parts of your schema from some users. This isn’t exactly part of the GraphQL spec, but it’s roughly within the bounds of the spec.
Here are some reasons you might want to hide parts of your schema:
You can customize the visibility of parts of your schema by reimplementing various visible?
methods:
.visible?(context)
class method#visible?(context)
instance method#visible?(context)
instance method.visible?(context)
class methodThese methods are called with the query context, based on the hash you pass as context:
. If the method returns false, then that member of the schema will be treated as though it doesn’t exist for the entirety of the query. That is:
Let’s say you’re working on a new feature which should remain secret for a while. You can implement .visible?
in a type:
class Types::SecretFeature < Types::BaseObject
def self.visible?(context)
# only show it to users with the secret_feature enabled
super && context[:viewer].feature_enabled?(:secret_feature)
end
end
(Always call super
to inherit the default behavior.)
Now, the following bits of GraphQL will return validation errors:
SecretFeature
, eg query { findSecretFeature { ... } }
SecretFeature
, eg Fragment SF on SecretFeature
And in introspection:
__schema { types { ... } }
will not include SecretFeature
__type(name: "SecretFeature")
will return nil
SecretFeature
will not include itSecretFeature
will be excluded from introspectionclass Types::BaseField < GraphQL::Schema::Field
# Pass `field ..., require_admin: true` to hide this field from non-admin users
def initialize(*args, require_admin: false, **kwargs, &block)
@require_admin = require_admin
super(*args, **kwargs, &block)
end
def visible?(ctx)
# if `require_admin:` was given, then require the current user to be an admin
super && (@require_admin ? ctx[:viewer]&.admin? : true)
end
end
For this to work, the base field class must be configured with other GraphQL types.
class Types::BaseArgument < GraphQL::Schema::Argument
# If `require_logged_in: true` is given, then this argument will be hidden from logged-out viewers
def initialize(*args, require_logged_in: false, **kwargs, &block)
@require_logged_in = require_logged_in
super(*args, **kwargs, &block)
end
def visible?(ctx)
super && (@require_logged_in ? ctx[:viewer].present? : true)
end
end
For this to work, the base argument class must be configured with other GraphQL types.
By default, GraphQL-Ruby always runs visibility checks. You can opt out of this by adding to your schema class:
class MySchema < GraphQL::Schema
# ...
# Opt out of GraphQL-Ruby's visibility feature:
use GraphQL::Schema::AlwaysVisible
end
For big schemas, this can be a worthwhile speed-up.