Table of Contents
Type Map
The type map is central for most of the things that happen in this gem. Its purpose is to keep track of everything that has been added to your application’s GraphQL area and ensure that components are properly accessible from their requested scope.
Note This page explains how the Type Map works, but you don’t need to know this to use the gem.
You can think about the Type Map as the central index of all the things defined for GraphQL. This index is also versioned, which means that cached resources should be invalidated if the Type Map has a different version than when the value was cached.
The Type Map index is composed of three parts:
namespace
- The namespace of the element
base_class
- One of
Type
,Directive
,Schema
key
- The key of the element
Rails::GraphQL.type_map.fetch!(
:string, # key
base_class: :Type, # base_class
namespace: :base, # namespace
)
The Type Map architecture was based on ActiveRecord::Type::TypeMap.
Registering
The registration process only happens when the index is consulted. Before that, components are
added to a postponed
registration, which happens when you inherit one of the GraphQL classes.
They are postponed because changes may occur after the class is inherited, and those changes
can affect registration.
Here is how you can check how many postponed objects are waiting to be registered:
:001 > Rails::GraphQL.type_map.inspect
=> #<Rails::GraphQL::TypeMap [index] ... @pending=3 ...>
When components are actually registered, all its namespaces, name, and possible aliases are added to the index. However, only one item actually points to the components, the others just simply points to that other value. This is how it works
@index[:base][:Type][:string] = Rails::GraphQL::Type::Scalar::StringScalar
@index[:base][:Type]['String'] = -> { @index[:base][:Type][:string] }
Aliases
At any time, you can add aliases to types. You have two options, an alias to another key or a resolution block that should be called when the alias key is requested. This is widely used to map database-specific types to GraphQL types.
Rails::GraphQL.type_map.register_alias(:str, :string)
# OR
Rails::GraphQL.type_map.register_alias(:str) do
Rails::GraphQL::Type::Scalar::StringScalar
end
Read more about ActiveRecord source
Un-registering
Due to the Rails reloader, the Type Map must know how to unregister objects from its index.
However, the process is quite simple because we can only assign a nil
value to the
outermost item of the index.
# This will guarantee a proper cleanup of the schema
Rails::GraphQL.type_map.unregister(GraphQL::AppSchema)
Register Hook
The Type Map allows you to add a hook for when an expected key is created. This allows you to
conditionally add fields and other components to objects requiring another to exist first.
The block will only be called when the namespace
and the base_class
match the ones
provided to the hook setup. The block will be called immediately if a key matching
this criteria already exists.
# This will be added as a hook
Rails::GraphQL.type_map.after_register(:str) do |object|
puts object.name # Rails::GraphQL::Type::Scalar::StringScalar
end
# This will trigger the above hook
Rails::GraphQL.type_map.register_alias(:str, :string)
# This will now be triggered immediately
Rails::GraphQL.type_map.after_register(:str) do |object|
puts object.name # Rails::GraphQL::Type::Scalar::StringScalar
end
Fetching
You have two options when fetching keys from the Type Map: fetch
and fetch!
.
The biggest difference between them is that the second one will load
dependencies after a first try, warn about the usage of a fallback, or
raise a NotFoundError
if unable to resolve the key.
They both will register all pending components before attempting to fetch.
:001 > Rails::GraphQL.type_map.fetch(:str)
=> nil
:002 > Rails::GraphQL.type_map.fetch!(:str)
=> # Unable to find :str Type object. (Rails::GraphQL::NotFoundError)
:002 > Rails::GraphQL.type_map.fetch!(:str, fallback: :string)
=> # [GraphQL] Type "str" is not defined, using String instead.
#<GraphQL::Scalar String>
Reading
There are two ways you can read what Type Map has registered:
objects(base_classes: nil, namespaces: nil)
Get the list of all objects given one or many base_classes
and namespaces
.
each_from(namespaces, exclusive: false, base_classes: nil) {}
Iterate over the objects from the given namespaces
and base_classes
.
The exclusive
argument prevents the :base
namespace from automatically being
considered.