Lee Hambley
Posted on July 30, 2020
This is a quick post of a self-contained MiniTest example of how to implement resolve_type
on a GraphQL::Schema
if you are using graphql-ruby
without using the Ruby DSL to define your schema.
graphql-ruby
provides two ways to create a schema, one from the .graphql
file directly (from_definition
) and by using the "class based definition"
Taking a Schema Definition Language (SDL) such as GraphQL and hiding it behind a Domain Specific Language (DSL) in a higher level turing-complete language strikes me as utterly irresponsible, the value of an SDL is in the interchange format, and for us that was an absolute necessity.
Many features of graphql-ruby
behave differently, or simply don't work at all, or are undocumented if you use the SDL approach rather than their preferred DSL, we run into issues with it from time to time.
In this case, without the DSL the Gem cannot infer what type
a resolver result has, and how to resolve that using the variant types from the union. It is required to implement a resolve_type
function in your resolver tree, but where and how to do that isn't obvious, I hope this post helps:
# frozen_string_literal: true
require 'test_helper'
module Schema
class UnionsFromDefinitionTest < Minitest::Test
def definition
<<~EOSCHEMA
type Wimpel {
id: ID!
}
type Doodad {
code: ID!
}
union Widget = Doodad | Wimpel
type Query {
widget: [Widget]!
}
EOSCHEMA
end
def resolvers
Sch3ma.default_resolvers.merge(
{
'Query' => {
'widget' => lambda { |_obj, _args, _ctx|
[OpenStruct.new(id: 'i am wimpel'), OpenStruct.new(code: 'i am doodad')]
}
},
'resolve_type' => lambda { |_type, obj, ctx|
type_name = if obj.respond_to? :id
'Wimpel'
elsif obj.respond_to? :code
'Doodad'
end
ctx.schema.types[type_name] || raise('boom')
}
}
)
end
def schema
GraphQL::Schema.from_definition(
definition,
default_resolve: resolvers
)
end
def test_querying_union_types
res = schema.execute(<<~EOQUERY)
query {
widget {
... on Wimpel {
id
}
... on Doodad {
code
}
}
}
EOQUERY
assert_equal({ 'widget' => [{ 'id' => 'i am wimpel' }, { 'code' => 'i am doodad' }] }, res['data'])
end
end
end
In reality our schema lives in a standlone package (Gem) and we have an implementation of the resolver map in our actual application, the schema is used by other teams too. This test-case in the schema repo helps serve as documentation on how this feature is supposed to be used with the SDL rather than the DSL.
Posted on July 30, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.