Defining the schema

GraphQL requires a graph-like schema to query against. The nodes and edges of the graph will be the objects and relationships in your API.

Using the Star Wars example from the GraphQL documentation, let’s assume we have a Django app with the following model structure:

  • Characters appear in Episodes.
  • Characters are friends with other Characters.

Adding nodes - Objects

Create an Object node for each of the models:

from django_graph_api import (
    Object,
)

class Episode(Object):
    ...

class Character(Object):
    ...

Add scalar fields to each of the nodes:

from django_graph_api import (
    Object,
    CharField,
    IntegerField,
)

class Episode(Object):
    name = CharField()
    number = IntegerField()

class Character(Object):
    name = CharField()

You can define any field on the node (Object) that is also a field or property of the model that it represents. You can also define custom logic to get a field’s value by adding a get_<field_name> method to the object. The current model instance will be available as self.data.

Arguments can be defined for fields by passing in <argname>=<graphql type instance>. The value passed in a query will be available as a keyword argument to the field value method if there is one.

::

from django_graph_api.graphql.types import Boolean

class Character(Object):

name = CharField(upper=Boolean())

def get_name(self, upper=False):
name = ‘{} {}’.format(
self.data.first_name, self.data.last_name,

) if upper:

return name.upper()

return name

Scalar field types

For scalar types, the type of the field determines how it will be returned by the API.

For example, if a model’s field is stored as an IntegerField on the Django model and defined as a CharField in the graph API, the model value will be coerced from an int to a str type when it is resolved.

Supported scalar types can be found in the API documentation and feature list.

Adding edges - Relationships

In order to traverse the nodes in your graph schema you need to define relationships between them.

This is done by adding related fields to your Object nodes. These non-scalar fields will return other objects or a list of objects.

  • If the field should return an object, use RelatedField
  • If the field should return a list of objects, use ManyRelatedField

When defining the object type of the related field, you can use:

  • The class of the object, e.g. appears_in = ManyRelatedField(Episode)
  • A callable that returns the class of the object, e.g. characters = ManyRelatedField(lambda: Character)
  • ‘self’, when you are referencing the current class, e.g. mother = RelatedField('self')

You can define any related field on the node (Object) that is also a field or property of the model that returns another model, list of models, or model manager. You can also define custom logic by adding a get_<field_name> method to the object. The current model instance will be available as self.data.

Examples

Many-to-many relationship

from django_graph_api import (
    ManyRelatedField,
)

class Episode(Object):
    characters = ManyRelatedField(lambda: Character)

class Character(Object):
    appears_in = ManyRelatedField(Episode)

Many-to-one relationship

from django_graph_api import (
    ManyRelatedField,
    RelatedField,
)

class Character(Object):
    mother = RelatedField('self')
    children = ManyRelatedField('self')

One-to-one relationship

from django_graph_api import (
    RelatedField,
)

from .models import {
    Episode as EpisodeModel
}

class Episode(Object):
    next = RelatedField('self')
    previous = RelatedField('self')

    def get_next(self):
        return EpisodeModel.objects.filter(number=self.data.number + 1).first()

    def get_previous(self):
        return EpisodeModel.objects.filter(number=self.data.number - 1).first()

Defining query roots

By defining query roots, you can control how the user can access the schema.

from django_graph_api import RelatedField
from .models import Character as CharacterModel
from .models import Episode as EpisodeModel

@schema.register_query_root
class QueryRoot(Object):
    hero = RelatedField(Character)

    def get_hero(self):
        return CharacterModel.objects.get(name='R2-D2')

Sample queries

You should now be able to create more complicated queries and make use of GraphQL’s nested objects feature.

{
    hero {
        friends {
            name
        }
        appears_in {
            name
            number
        }
    }
}