Skip to main content

Overview

The Rails::GraphQL::Directive class provides a way to define directives that can modify the behavior of schema elements or execution. Directives work as event listeners and triggers. Namespace: Rails::GraphQL Extends:
  • ActiveSupport::Autoload
  • Helpers::WithEvents
  • Helpers::WithCallbacks
  • Helpers::WithArguments
  • Helpers::WithGlobalID
  • Helpers::Registerable

Directive Locations

Directives can be placed on various schema elements:

Class Attributes

repeatable
Boolean
default:"false"
Whether the directive can be used multiple times on the same location
class MyDirective < Rails::GraphQL::Directive
  self.repeatable = true
end

Class Methods

kind

Return the kind of object, always :directive. Returns: Symbol
Directive.kind # => :directive

base_type

Return the base directive class. Returns: Rails::GraphQL::Directive
MyDirective.base_type # => Rails::GraphQL::Directive
Alias: gid_base_class

gql_name

Get the GraphQL name of the directive (camelCase with lowercase first letter). Returns: String
DeprecatedDirective.gql_name # => "deprecated"
CachedDirective.gql_name # => "cached"

locations

Get the set of valid locations for this directive. Returns: Set<Symbol>
Directive.locations # => Set[:field, :fragment_definition, ...]

placed_on

Add valid locations for the directive (merges with inherited locations).
list
Array<Symbol>
required
Locations where the directive can be used
class CachedDirective < Rails::GraphQL::Directive
  placed_on :field, :field_definition
end

placed_on!

Override valid locations for the directive (replaces inherited locations).
list
Array<Symbol>
required
Locations where the directive can be used
class CustomDirective < Rails::GraphQL::Directive
  placed_on! :field, :object
end

build

Build a directive instance with parsed arguments.
xargs
Hash
required
Arguments as key-value pairs
Returns: Directive instance
directive = DeprecatedDirective.build(reason: 'Use newField instead')

find_by_gid

Find and optionally instantiate a directive by its Global ID.
gid
GlobalID
required
The Global ID object
Returns: Directive class or instance
directive = Directive.find_by_gid(gid)

Constructor

initialize

args
OpenStruct
Positional arguments struct
xargs
Hash
Named arguments as keyword arguments
# Using keyword arguments
directive = DeprecatedDirective.new(reason: 'Use newField instead')

# Shortcut using callable class
directive = DeprecatedDirective(reason: 'Use newField instead')

Instance Attributes

args
OpenStruct
Arguments provided to the directive instance (frozen)
event
Event
Current event context (if within event callback)

Instance Methods

assign_owner!

Assign the owner of the directive (can only be called once).
owner
Object
required
The schema element that owns this directive
Raises: ArgumentError if owner already assigned
directive.assign_owner!(field)

args_as_json

Convert all arguments to their JSON representation. Returns: Hash
directive.args_as_json # => { "reason" => "Use newField instead" }

args_to_json

Convert all arguments to their JSON string representation. Returns: Hash
directive.args_to_json # => { "reason" => "\"Use newField instead\"" }

all_events

Get all events with the directive instance as callback context. Returns: Hash - Event name to callbacks mapping
events = directive.all_events

validate!

Validate the directive instance and its arguments. Raises: ArgumentError if directive is unbounded or has invalid arguments
directive.validate!

+

Combine directives into an array.
other
Directive
required
Another directive to combine
Returns: Array<Directive> Alias: &
combined = DeprecatedDirective(reason: 'Old') + CachedDirective(ttl: 60)
# => [#<@deprecated>, #<@cached>]

Delegation

The following methods are delegated to the class:
  • locations - Get valid locations
  • gql_name - Get GraphQL name
  • gid_base_class - Get base class
  • repeatable? - Check if repeatable

Event Filters

Directives support event filters to control when they trigger:

:for Filter

Trigger directive for specific types.
on :resolve, for: User do
  # Only triggered when resolving User type
end

:on Filter

Trigger directive on specific events.
on :resolve, on: Field do
  # Only triggered when resolving fields
end

:during Filter

Trigger directive during specific phases.
on :resolve, during: :execution do
  # Only triggered during execution phase
end

Built-in Directives

@deprecated

Marks fields or enum values as deprecated.
reason
String
Explanation for deprecation
field :old_field, :string, directives: DeprecatedDirective(reason: 'Use newField instead')

@include

Conditionally include fields in query execution.
if
Boolean
required
Include field if true
query {
  user {
    name
    email @include(if: $includeEmail)
  }
}

@skip

Conditionally skip fields in query execution.
if
Boolean
required
Skip field if true
query {
  user {
    name
    email @skip(if: $skipEmail)
  }
}

@specifiedBy

Provides a URL specification for scalar types.
url
String
required
URL to the scalar specification
class UUID < Rails::GraphQL::Type::Scalar
  directives SpecifiedByDirective(url: 'https://tools.ietf.org/html/rfc4122')
end

@cached

Cache field resolution results.
ttl
Int
Time to live in seconds
field :expensive_calculation, :int, directives: CachedDirective(ttl: 300)

Example: Custom Directive

class AuthorizeDirective < Rails::GraphQL::Directive
  desc 'Checks if the user has required permissions'

  # Can be used on fields and field definitions
  placed_on :field, :field_definition

  # Define arguments
  argument :permission, :string, null: false
  argument :resource, :string

  # Can be used multiple times
  self.repeatable = true

  # Event callback - triggered before field resolution
  on :resolve, during: :prepare do
    user = event.request.context.current_user
    
    unless user.has_permission?(args.permission, args.resource)
      raise GraphQL::UnauthorizedError, "Missing permission: #{args.permission}"
    end
  end
end

# Usage in schema
class User < Rails::GraphQL::Type::Object
  field :email, :string, directives: AuthorizeDirective(permission: 'read:email')
  
  field :ssn, :string, directives: [
    AuthorizeDirective(permission: 'read:pii'),
    AuthorizeDirective(permission: 'read:ssn')
  ]
end

Example: Rate Limiting Directive

class RateLimitDirective < Rails::GraphQL::Directive
  desc 'Limits the rate of field requests'

  placed_on :field_definition

  argument :limit, :int, null: false, desc: 'Maximum requests per window'
  argument :window, :int, default: 60, desc: 'Time window in seconds'

  on :resolve, during: :prepare do
    key = "rate_limit:#{event.field.gql_name}:#{event.request.context.user_id}"
    
    count = Rails.cache.increment(key, 1, expires_in: args.window.seconds)
    
    if count > args.limit
      raise GraphQL::RateLimitError, "Rate limit exceeded for #{event.field.gql_name}"
    end
  end
end

# Usage
class Query < Rails::GraphQL::Type::Object
  field :search, [Result], directives: RateLimitDirective(limit: 100, window: 60) do
    argument :term, :string, null: false
  end
end

Example: Logging Directive

class LogDirective < Rails::GraphQL::Directive
  desc 'Logs field resolution timing'

  placed_on :field, :field_definition

  argument :level, :string, default: 'info'

  on :resolve, during: :before do
    event[:start_time] = Time.current
  end

  on :resolve, during: :after do
    duration = Time.current - event[:start_time]
    
    Rails.logger.public_send(
      args.level,
      "Field #{event.field.gql_name} resolved in #{duration}s"
    )
  end
end

# Usage
field :expensive_query, :Result, directives: LogDirective(level: 'debug')

Build docs developers (and LLMs) love