Directives
Directives are system-defined keywords with two kinds of uses:
-
runtime directives modify execution, so that when they are present, the
GraphQLruntime does something different; -
schema directives annotate schema definitions, indicating different configurations or metadata about schemas and types.
Runtime Directives
Runtime directives are server-defined keywords that modify GraphQL execution. All GraphQL systems have at least two directives, @skip and @include. For example:
query ProfileView($renderingDetailedProfile: Boolean!){ viewer { handle # These fields will be included only if the check passes: ... @include(if: $renderingDetailedProfile) { location homepageUrl } } }
Here’s how the two built-in directives work:
-
@skip(if: ...)skips the selection if theif: ...value is truthy (GraphQL::Schema::Directive::Skip) -
@include(if: ...)includes the selection if theif: ...value is truthy (GraphQL::Schema::Directive::Include)
Custom Runtime Directives
Custom directives extend GraphQL::Schema::Directive:
# app/graphql/directives/my_directive.rb class Directives::MyDirective < GraphQL::Schema::Directive description "A nice runtime customization" location FIELD end
Then, they’re hooked up to the schema using directive(...):
class MySchema < GraphQL::Schema # Attach the custom directive to the schema directive(Directives::MyDirective) end
And you can reference them in the query with @myDirective(...):
query { field @myDirective { id } }
GraphQL::Schema::Directive::Feature and GraphQL::Schema::Directive::Transform are included in the library as examples.
Runtime hooks
Directive classes may implement the following class methods to interact with the runtime:
-
def self.include?(obj, args, ctx): If this hook returnsfalse, the nodes flagged by this directive will be skipped at runtime. -
def self.resolve(obj, args, ctx): Wraps the resolution of flagged nodes. Resolution is passed as a block, soyieldwill continue resolution.
Looking for a runtime hook that isn’t listed here? open an issue to start the conversation!
Schema Directives
Schema directives are used in GraphQL’s interface definition language (IDL). For example, @deprecated is built in to GraphQL-Ruby:
type User { firstName @deprecated(reason: "Use `name` instead") lastName @deprecated(reason: "Use `name` instead") name }
In the schema definition, directives express metadata about types, fields, and arguments.
Custom Schema Directives
To make a custom schema directive, extend GraphQL::Schema::Directive:
# app/graphql/directives/permission.rb class Directives::Permission < GraphQL::Schema::Directive argument :level, String locations FIELD_DEFINITION, OBJECT end
Then, attach it to parts of your schema with directive(...):
class Types::JobPosting < Types::BaseObject directive Directives::Permission, level: "manager" end
Arguments and fields also accept a directives: keyword:
field :salary, Integer, null: false, directives: { Directives::Permission => { level: "manager" } }
After that:
-
the configured object’s
.directivesmethod will return an array containing an instance of the specified directive -
IDL dumps (from Schema.to_definition) will include the configured directives
Similarly, Schema.from_definition parses directives from IDL strings.
For a couple of built-in examples, check out:
-
GraphQL::Schema::Directive::Deprecatedwhich implementsdeprecation_reason(viaGraphQL::Schema::Member::HasDeprecationReason) -
GraphQL::Schema::Directive::Flagged, which is an example of using schema directives to implement visibility
Custom Name
By default, the directive’s name is taken from the class name. You can override this with graphql_name, for example:
class Directives::IsPrivate < GraphQL::Schema::Directive graphql_name "someOtherName" end
Arguments
Like fields, directives may have arguments :
argument :if, Boolean, description: "Skips the selection if this condition is true"