Skip to main content

Overview

Rails GraphQL implements its own version of Rails GlobalID, uniquely identifying GraphQL components within your application. This feature is essential for request caching, compilation, and ActiveJob serialization.
Global IDs use a custom gql:// URI scheme specifically designed for GraphQL components.

URI Format

Global IDs follow this structure:
gql://namespace/class_name/scope?/name?params?

Examples

"gql://base/Type/String"
"gql://base/Type/User"
"gql://admin/Type/CustomScalar"

URI Components

schema
string
required
Always gql for GraphQL components
namespace
string
required
Primary namespace in dash-case format (e.g., base, admin-api)
class_name
string
required
Top-level class: Type, Directive, or Schema
scope
string
For schema fields only: query, mutation, or subscription
name
string
required
Symbolized name of the object
params
string
URI-encoded parameters (rarely used)

Creating Global IDs

From Objects

# From lib/rails/graphql/global_id.rb
class << self
  # Create a new GraphQL Global identifier
  def create(object, options = nil)
    scope = options&.delete(:scope) || scope_of(object)
    new(URI::GQL.create(object, scope, options), options)
  end
  
  # Find the scope on which the object is applied to
  def scope_of(object)
    object.try(:schema_type) if object.gid_base_class.is_a?(Helpers::WithSchemaFields)
  end
end
GraphQL::AppSchema[:query][:welcome].to_gid.to_s
# => "gql://base/Schema/query/welcome"

GraphQL::Type::Scalar::StringScalar.to_gid.to_s
# => "gql://base/Type/String"

Finding Components

By Global ID String

# From lib/rails/graphql/global_id.rb
def find(options = {})
  base_class.try(:find_by_gid, self)
end

def base_class
  if %w[Schema Directive Type].include?(class_name)
    GraphQL.const_get(class_name, false)
  else
    GraphQL.type_map.fetch(class_name, namespace: namespace)
  end
end
GraphQL::GlobalID.find("gql://base/Type/String")
# => Rails::GraphQL::Type::Scalar::StringScalar

Interactive Example

GraphQL::AppSchema[:query][:welcome].to_gid.to_s
# => "gql://base/Schema/query/welcome"

GraphQL::GlobalID.find("gql://base/Schema/query/welcome")
# => #<Rails::GraphQL::Field::OutputField
#    GraphQL::AppSchema[:query]
#    welcome: String!>

ActiveJob Serialization

Global IDs integrate with ActiveJob for passing GraphQL objects to background jobs:
# From lib/rails/graphql/global_id.rb
class Serializer
  include Singleton
  
  # Determines if an argument should be serialized
  def serialize?(argument)
    argument.is_a?(Helpers::WithGlobalID) || argument.class.is_a?(Helpers::WithGlobalID)
  end
  
  # Serializes to JSON primitive
  def serialize(argument)
    { GlobalID::SERIALIZER_KEY => argument.to_global_id.to_s }
  end
  
  # Deserializes from JSON
  def deserialize(argument)
    GlobalID.find argument[GlobalID::SERIALIZER_KEY]
  end
end
class ProcessFieldJob < ApplicationJob
  def perform(field)
    # field is automatically deserialized from GID
    puts field.gql_name
  end
end

# Automatically serializes to GID
field = GraphQL::AppSchema[:query][:welcome]
ProcessFieldJob.perform_later(field)

Usage in Caching

Global IDs are used extensively for request caching:
# Cache key includes GID
cache_key = [
  field.to_gid.to_s,
  arguments.hash,
  context_hash
].join('/')

Rails.cache.fetch(cache_key) do
  # Expensive operation
end
Global IDs provide stable, unique identifiers that work across processes and servers.

Helpers Module

Components include the WithGlobalID helper:
module Helpers::WithGlobalID
  def to_global_id(options = {})
    GlobalID.create(self, options)
  end
  
  alias to_gid to_global_id
  
  def gid_base_class
    self.class
  end
end
Any class including Helpers::WithGlobalID can be serialized to ActiveJob.

URI Parsing

def initialize(gid, options = {})
  @uri = gid.is_a?(URI::GQL) ? gid : URI::GQL.parse(gid)
end

delegate :namespace, :class_name, :scope, :name, :instantiate?, to: :uri
gid = GraphQL::GlobalID.new("gql://admin/Schema/query/users")

gid.namespace
# => "admin"

gid.class_name
# => "Schema"

gid.scope
# => "query"

gid.name
# => "users"

Best Practices

Use for Stable References

Global IDs provide stable references across processes:
# Store in cache or database
cached_field_gid = field.to_gid.to_s

# Retrieve later
field = GraphQL::GlobalID.find(cached_field_gid)

Background Jobs

Pass GraphQL components to jobs safely:
ProcessSchemaJob.perform_later(GraphQL::AppSchema)
# Automatically serializes/deserializes via GID

Version Awareness

Remember that Global IDs don’t include version information:
# If schema changes, cached GIDs may become invalid
# Use Type Map version for cache invalidation
cache_key = [field.to_gid.to_s, GraphQL.type_map.version].join('/')

Limitations

Future Implementation: The current Global ID implementation is the beginning of a larger system. Future versions will support:
  • Signed Global IDs for security
  • More component types
  • Versioned Global IDs
  • Cross-application Global IDs

Type Map

How Global IDs resolve components

Request Caching

Using Global IDs for caching

Build docs developers (and LLMs) love