Table of Contents
Schemas
schema {
query: _Query
mutation: _Mutation
subscription: _Subscription
}
Schema is the place where you will coordinate what features are available. The features of a schema are divided into configuring, fields, dependencies, subscriptions, cache, error handling, inline types, and type map interaction.
Configuring
Some of the settings can be configured at a per schema level. The application can have one value set globally, and the schema can have its own.
Here is the list of such settings:
cache
enable_introspection
request_strategies
enable_string_collector
default_response_format
schema_type_names
default_subscription_provider
default_subscription_broadcastable
Here is the list of settings exclusive for the schemas:
subscription_provider
: The instance of the subscription provider setup manually. Defaultnil
.
You can use the block approach or direct assignment to change the settings.
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
# Direct access
config.enable_string_collector = false
# Block approach
configure do |config|
config.enable_string_collector = false
end
end
end
Fields
The fields of a schema are divided into 3 groups: query
, mutation
, and subscription
.
This gem does not require you to set up a type for each one of this collection of fields.
Schemas use a shared concept, which has a collection of methods and one instance variable
per each group.
Inside of your schema, you can easily add fields by just calling their supporting methods:
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
query_fields do
field(:a_query, :string)
end
mutation_fields do
field(:a_mutation, :string)
end
subscription_fields do
field(:a_subscription, :string)
end
end
end
However, this gem provides several other ways for you to declare your fields. You should use this only for the simplest things.
Read more about fields, alternatives, and field lists.
Dependencies
Dependencies are how you inform the Type Map that other files should be loaded before a schema can be fully validated. There are 2 ways of informing dependencies, plus 1 way of importing things into your schema.
This is crucial for the organization of your application.
Known Dependencies
Known dependencies are types provided by this or other gems that are not loaded by default. Types that the Type Map doesn’t know about will be treated as pure strings (and you will get a warning about it).
Here is how you can load these dependencies:
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
# Inform the type and the names that should be loaded
load_scalars :bigint, :date_time
# OR
load_dependencies :scalar, :bigint, :date_time
end
end
The list of available dependencies can be found on config.known_dependencies
.
Local Dependencies
Local dependencies are other directories containing types necessary for the schema. Different from Rails, GraphQL needs to know everything it has available. Therefore, all its objects, types, and inputs must be loaded beforehand. The explicit load was intentional.
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
# Load one specific directory recursively
load_directory 'objects'
# Load only that directory non-recursively
load_directory 'objects', recursive: false
# Load more than one directory at once
load_directory 'objects', 'inputs'
# With no arguments, it will load the current directory recursively
load_directory
# Same as
load_directory '.', recursive: true
# OR
load_current_directory
end
end
Files will be loaded when a request is processed and the types involved can’t be found straightaway.
Importing Fields
As mentioned before, schema fields can be defined in different ways. When declared outside of the schema, they need to be properly imported into it. One method imports a single field into a specific type, and the other imports the whole module and all fields declared in it to their proper places. These methods can be tuned to fit your needs.
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
# Import a field declared as a class into this schema
import_into :query, GraphQL::Queries::Sample
# Import the whole module into the schema, non-recursively
import_all GraphQL::Queries
# Import the whole module into the schema recursively
import_all GraphQL::Queries, recursive: true
end
end
Sources do not require importing because they have their own mechanism for publishing their fields.
Read more about alternatives and importing fields.
Subscriptions
Subscriptions will work with ActionCable provider and Memory store by default. The methods provided in the schema mostly work as a bridge between the requests and the provider configured.
What matters the most for the schema is the provider, and you can set up one using the config of the schema.
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
# This is the default behavior when the config is left as nil
config.subscription_provider =
Rails::GraphQL::Subscription::Provider::ActionCable.new(
cable: ::ActionCable,
prefix: 'graphql',
store: Rails::GraphQL::Subscription::Store::Memory.new,
logger: GraphQL::AppSchema.logger,
)
end
end
Read more about subscriptions providers.
Cache
The cache is crucial for schemas that deal with subscriptions.
Some other features also rely on the cache to improve performance and strictness of requests.
However, the methods provided for the schema are simply a bridge between the requests and the configured cache provider.
By default, the schema will rely on config.cache
.
# Schema methods available for caching
GraphQL::AppSchema.subscription_id_for(*) # => SecureRandom.uuid
GraphQL::AppSchema.cached?(*) # => config.cache.exist?(*)
GraphQL::AppSchema.delete_from_cache(*) # => config.cache.delete(*)
GraphQL::AppSchema.read_from_cache(*) # => config.cache.read(*)
GraphQL::AppSchema.write_on_cache(*) # => config.cache.write(*)
GraphQL::AppSchema.fetch_from_cache(*) # => config.cache.fetch(*)
As long as the config.cache
object setup complies with
ActiveSupport Cache Store,
you won’t need to override these methods.
Error Handling
Schemas implement ActiveSupport::Rescuable. Therefore, for exception handling, it will behave as a controller.
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
rescue_from ActiveRecord::RecordNotFound do |exception|
# The message of the error
exception.message.inspect
# Likely the field of the error
exception.source.inspect
# The running request where the error occurred
exception.request.inspect
end
end
end
By default, all the exceptions will be turned into proper errors in the response, plus you will receive a nice backtrace display in your logs.
Read more about request logs.
Inline Types
Inside your schema, you can quickly define types. There are some use cases that this can be a good approach. However, it’s recommended to break large schemas into several files and use local dependencies instead.
# app/graphql/app_schema.rb
module GraphQL
class AppSchema < GraphQL::Schema
enum 'Episode' do
desc 'One of the films in the Star Wars Trilogy'
add 'NEW_HOPE', desc: 'Released in 1977.'
add 'EMPIRE', desc: 'Released in 1980.'
add 'JEDI', desc: 'Released in 1983.'
end
object 'Human' do
desc 'A humanoid creature in the Star Wars universe'
field :id, null: false, desc: 'The id of the human'
field :name, desc: 'The name of the human'
end
end
end
This is recommended only for really small schemas or for testing purposes. It can also be used to demonstrate issues with the gem.
Read more about inline types and recommendations.
Type Map Interaction
Schemas interacts with the Type Map to resolve the types within its components. It’s pretty much a shortcut, adding the namespace before fetching from the Type Map. However, it is the recommended way of using it.
GraphQL::AppSchema.find_type(:string)
GraphQL::AppSchema.find_type!(:string)
GraphQL::AppSchema.find_directive!('deprecated')
Regardless, this is something other than what you will be interacting. In normal circumstances, fields will know how to solve their types through their schemas.
Read more about the Type Map.
Others
There are a couple of other methods available for schemas:
to_gql
Prints the whole schema and its structure in a formatted GraphQL syntax. Great for debugging. See an example on the controller.
introspection?
Checks if the current schema has introspection enabled.
enable_introspection!
Enable introspection for the current schema.
Read more about introspection.