ObjectType¶
A Graphene ObjectType is the building block used to define the relationship between Fields in your Schema and how their data is retrieved.
The basics:
- Each ObjectType is a Python class that inherits from
graphene.ObjectType
. - Each attribute of the ObjectType represents a
Field
. - Each
Field
has a resolver method to fetch data (or Default Resolver).
Quick example¶
This example model defines a Person, with a first and a last name:
from graphene import ObjectType, String
class Person(ObjectType):
first_name = String()
last_name = String()
full_name = String()
def resolve_full_name(parent, info):
return f"{parent.first_name} {parent.last_name}"
This ObjectType defines the field first_name, last_name, and full_name. Each field is specified as a class attribute, and each attribute maps to a Field. Data is fetched by our resolve_full_name
resolver method for full_name
field and the Default Resolver for other fields.
The above Person
ObjectType has the following schema representation:
type Person {
firstName: String
lastName: String
fullName: String
}
Resolvers¶
A Resolver is a method that helps us answer Queries by fetching data for a Field in our Schema.
Resolvers are lazily executed, so if a field is not included in a query, its resolver will not be executed.
Each field on an ObjectType in Graphene should have a corresponding resolver method to fetch data. This resolver method should match the field name. For example, in the Person
type above, the full_name
field is resolved by the method resolve_full_name
.
Each resolver method takes the parameters:
- Parent Value Object (parent) for the value object use to resolve most fields
- GraphQL Execution Info (info) for query and schema meta information and per-request context
- GraphQL Arguments (**kwargs) as defined on the Field.
Resolver Parameters¶
Parent Value Object (parent)¶
This parameter is typically used to derive the values for most fields on an ObjectType.
The first parameter of a resolver method (parent) is the value object returned from the resolver of the parent field. If there is no parent field, such as a root Query field, then the value for parent is set to the root_value
configured while executing the query (default None
). See Executing a query for more details on executing queries.
Resolver example¶
If we have a schema with Person type and one field on the root query.
from graphene import ObjectType, String, Field
class Person(ObjectType):
full_name = String()
def resolve_full_name(parent, info):
return f"{parent.first_name} {parent.last_name}"
class Query(ObjectType):
me = Field(Person)
def resolve_me(parent, info):
# returns an object that represents a Person
return get_human(name="Luke Skywalker")
When we execute a query against that schema.
schema = Schema(query=Query)
query_string = "{ me { fullName } }"
result = schema.execute(query_string)
assert result.data["me"] == {"fullName": "Luke Skywalker"}
Then we go through the following steps to resolve this query:
parent
is set with the root_value from query execution (None).Query.resolve_me
called withparent
None which returns a value objectPerson("Luke", "Skywalker")
.- This value object is then used as
parent
while callingPerson.resolve_full_name
to resolve the scalar String value “Luke Skywalker”. - The scalar value is serialized and sent back in the query response.
Each resolver returns the next Parent Value Object (parent) to be used in executing the following resolver in the chain. If the Field is a Scalar type, that value will be serialized and sent in the Response. Otherwise, while resolving Compound types like ObjectType, the value be passed forward as the next Parent Value Object (parent).
Naming convention¶
This Parent Value Object (parent) is sometimes named obj
, parent
, or source
in other GraphQL documentation. It can also be named after the value object being resolved (ex. root
for a root Query or Mutation, and person
for a Person value object). Sometimes this argument will be named self
in Graphene code, but this can be misleading due to Implicit staticmethod while executing queries in Graphene.
GraphQL Execution Info (info)¶
The second parameter provides two things:
- reference to meta information about the execution of the current GraphQL Query (fields, schema, parsed query, etc.)
- access to per-request
context
which can be used to store user authentication, data loader instances or anything else useful for resolving the query.
Only context will be required for most applications. See Context for more information about setting context.
GraphQL Arguments (**kwargs)¶
Any arguments that a field defines gets passed to the resolver function as keyword arguments. For example:
from graphene import ObjectType, Field, String
class Query(ObjectType):
human_by_name = Field(Human, name=String(required=True))
def resolve_human_by_name(parent, info, name):
return get_human(name=name)
You can then execute the following query:
query {
humanByName(name: "Luke Skywalker") {
firstName
lastName
}
}
Note: There are several arguments to a field that are “reserved” by Graphene
(see Fields (Mounted Types)).
You can still define an argument that clashes with one of these fields by using
the args
parameter like so:
from graphene import ObjectType, Field, String
class Query(ObjectType):
answer = String(args={'description': String()})
def resolve_answer(parent, info, description):
return description
Convenience Features of Graphene Resolvers¶
Implicit staticmethod¶
One surprising feature of Graphene is that all resolver methods are treated implicitly as staticmethods. This means that, unlike other methods in Python, the first argument of a resolver is never self
while it is being executed by Graphene. Instead, the first argument is always Parent Value Object (parent). In practice, this is very convenient as, in GraphQL, we are almost always more concerned with the using the parent value object to resolve queries than attributes on the Python object itself.
The two resolvers in this example are effectively the same.
from graphene import ObjectType, String
class Person(ObjectType):
first_name = String()
last_name = String()
@staticmethod
def resolve_first_name(parent, info):
'''
Decorating a Python method with `staticmethod` ensures that `self` will not be provided as an
argument. However, Graphene does not need this decorator for this behavior.
'''
return parent.first_name
def resolve_last_name(parent, info):
'''
Normally the first argument for this method would be `self`, but Graphene executes this as
a staticmethod implicitly.
'''
return parent.last_name
# ...
If you prefer your code to be more explicit, feel free to use @staticmethod
decorators. Otherwise, your code may be cleaner without them!
Default Resolver¶
If a resolver method is not defined for a Field attribute on our ObjectType, Graphene supplies a default resolver.
If the Parent Value Object (parent) is a dictionary, the resolver will look for a dictionary key matching the field name. Otherwise, the resolver will get the attribute from the parent value object matching the field name.
from collections import namedtuple
from graphene import ObjectType, String, Field, Schema
PersonValueObject = namedtuple("Person", ["first_name", "last_name"])
class Person(ObjectType):
first_name = String()
last_name = String()
class Query(ObjectType):
me = Field(Person)
my_best_friend = Field(Person)
def resolve_me(parent, info):
# always pass an object for `me` field
return PersonValueObject(first_name="Luke", last_name="Skywalker")
def resolve_my_best_friend(parent, info):
# always pass a dictionary for `my_best_fiend_field`
return {"first_name": "R2", "last_name": "D2"}
schema = Schema(query=Query)
result = schema.execute('''
{
me { firstName lastName }
myBestFriend { firstName lastName }
}
''')
# With default resolvers we can resolve attributes from an object..
assert result.data["me"] == {"firstName": "Luke", "lastName": "Skywalker"}
# With default resolvers, we can also resolve keys from a dictionary..
assert result.data["myBestFriend"] == {"firstName": "R2", "lastName": "D2"}
Advanced¶
GraphQL Argument defaults¶
If you define an argument for a field that is not required (and in a query
execution it is not provided as an argument) it will not be passed to the
resolver function at all. This is so that the developer can differentiate
between a undefined
value for an argument and an explicit null
value.
For example, given this schema:
from graphene import ObjectType, String
class Query(ObjectType):
hello = String(required=True, name=String())
def resolve_hello(parent, info, name):
return name if name else 'World'
And this query:
query {
hello
}
An error will be thrown:
TypeError: resolve_hello() missing 1 required positional argument: 'name'
You can fix this error in several ways. Either by combining all keyword arguments into a dict:
from graphene import ObjectType, String
class Query(ObjectType):
hello = String(required=True, name=String())
def resolve_hello(parent, info, **kwargs):
name = kwargs.get('name', 'World')
return f'Hello, {name}!'
Or by setting a default value for the keyword argument:
from graphene import ObjectType, String
class Query(ObjectType):
hello = String(required=True, name=String())
def resolve_hello(parent, info, name='World'):
return f'Hello, {name}!'
One can also set a default value for an Argument in the GraphQL schema itself using Graphene!
from graphene import ObjectType, String
class Query(ObjectType):
hello = String(
required=True,
name=String(default_value='World')
)
def resolve_hello(parent, info, name):
return f'Hello, {name}!'
Resolvers outside the class¶
A field can use a custom resolver from outside the class:
from graphene import ObjectType, String
def resolve_full_name(person, info):
return f"{person.first_name} {person.last_name}"
class Person(ObjectType):
first_name = String()
last_name = String()
full_name = String(resolver=resolve_full_name)
Instances as value objects¶
Graphene ObjectType
s can act as value objects too. So with the
previous example you could use Person
to capture data for each of the ObjectType‘s fields.
peter = Person(first_name='Peter', last_name='Griffin')
peter.first_name # prints "Peter"
peter.last_name # prints "Griffin"
Field camelcasing¶
Graphene automatically camelcases fields on ObjectType from field_name
to fieldName
to conform with GraphQL standards. See Auto camelCase field names for more information.
ObjectType Configuration - Meta class¶
Graphene uses a Meta inner class on ObjectType to set different options.
GraphQL type name¶
By default the type name in the GraphQL schema will be the same as the class name
that defines the ObjectType
. This can be changed by setting the name
property on the Meta
class:
from graphene import ObjectType
class MyGraphQlSong(ObjectType):
class Meta:
name = 'Song'
GraphQL Description¶
The schema description of an ObjectType can be set as a docstring on the Python object or on the Meta inner class.
from graphene import ObjectType
class MyGraphQlSong(ObjectType):
''' We can set the schema description for an Object Type here on a docstring '''
class Meta:
description = 'But if we set the description in Meta, this value is used instead'
Interfaces & Possible Types¶
Setting interfaces
in Meta inner class specifies the GraphQL Interfaces that this Object implements.
Providing possible_types
helps Graphene resolve ambiguous types such as interfaces or Unions.
See Interfaces for more information.
from graphene import ObjectType, Node
Song = namedtuple('Song', ('title', 'artist'))
class MyGraphQlSong(ObjectType):
class Meta:
interfaces = (Node, )
possible_types = (Song, )