Skip to content

Schema Declaration

Inspired by Strawberry, Pygraphy uses dataclass to define the schema. All type classes can be printed as a valid GraphQL SDL. Pygraphy would try to translate all fields name from snake case to camel case.

import pygraphy


class Patron(pygraphy.Object):
    id: str
    name: str
    age: int


class Query(pygraphy.Query):
    """
    Query doc also can be printed
    """

    @pygraphy.field
    async def patron(self) -> Patron:
        """
        Return the patron
        """
        return Patron(id='1', name='Gwo', age=25)

    @pygraphy.field
    async def exception(self, content: str) -> str:
        raise RuntimeError(content)

Object

Declare Object schema by inheriting pygraphy.Object, Pygraphy checks the type signature by using Python type annotation and generate the SDL. Pygraphy supports adding the description to an Object class and its resolver fields.

from pygraphy import Object, field
from typing import Optional


class Node(Object):
    """
    A node with at most one sub node.
    """
    value: int
    description: str

    @field
    def sub_node(self, description: str = 'A node') -> Optional['Node']:
        """
        The resolver of getting sub node.
        """
        if self.value != 0:
            return Node(value-1, description=description)
        return None


assert str(Node) == '''"""
A node with at most one sub node.
"""
type Node {
  value: Int!
  description: String!
  "The resolver of getting sub node."
  subNode(
    description: String! = "A node"
  ): Node
}'''

A class method marked with pygraphy.field decorator would be treated as a resolver field.

Query

Query type is a subclass of Object, and it implements two built-in resolver fields: __schema and __type to support GraphQL Introspection.

from pygraphy import Query as BaseQuery, field


class Query(BaseQuery):
    """
    Query object
    """

    @field
    async def foo(self) -> int:
        """
        Return an int
        """
        return 1

Please note that Query class must not contain any non-resolver field.

Schema

A Schema class contains two non-resolver field: query and mutation.

from pygraphy import Schema as BaseSchema


class Schema(pygraphy.Schema):
    query: Optional[Query]
    mutation: Optional[Mutation]

Both query and mutation field must be optional, because of following the GraphQL Spec, every root node of query should be allowed to return null when an error raising during the request.

Types which are referenced with a Schema definition whether directly or indirectly would be all registered into Schema class, the SDL of those types will be generated with Schema class together.

import pygraphy
from typing import List, Optional


class Episode(pygraphy.Enum):
    NEWHOPE = 4
    EMPIRE = 5
    JEDI = 6


class Character(pygraphy.Interface):
    """
    Character interface contains human and droid
    """
    id: str
    name: str
    appears_in: List[Episode]

    @pygraphy.field
    def friends(self) -> Optional[List['Character']]:
        return None


class Human(pygraphy.Object, Character):
    """
    Human object
    """
    home_planet: str


class Droid(pygraphy.Object, Character):
    """
    Driod object
    """
    primary_function: str


class Query(pygraphy.Query):

    @pygraphy.field
    def hero(self, episode: Episode) -> Optional[Character]:
        return None

    @pygraphy.field
    def human(self, id: str = '1234') -> Optional[Human]:
        return Human(
            id=id, name='foo', appears_in=[Episode.NEWHOPE, Episode.EMPIRE], home_planet='Mars'
        )

    @pygraphy.field
    def droid(self, id: str) -> Optional[Droid]:
        return None


class Schema(pygraphy.Schema):
    query: Optional[Query]


print(Schema)
'''
Query()
"""
type Query {
  __schema: __Schema!
  __type(
    name: String!
  ): __Type
  droid(
    id: String!
  ): Droid
  hero(
    episode: Episode!
  ): Character
  human(
    id: String! = "1234"
  ): Human
}

(...other built-in types)

"""
Driod object
"""
type Droid implements Character {
  id: String!
  name: String!
  appearsIn: [Episode!]!
  primaryFunction: String!
  friends: [Character!]
}

"""
An enumeration.
"""
enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

"""
Character interface contains human and droid
"""
interface Character {
  id: String!
  name: String!
  appearsIn: [Episode!]!
  friends: [Character!]
}

"""
Human object
"""
type Human implements Character {
  id: String!
  name: String!
  appearsIn: [Episode!]!
  homePlanet: String!
  friends: [Character!]
}

"""
Schema(query: Union[__main__.Query, NoneType])
"""
schema {
  query: Query
}
'''

Subscribable Schema

Schema class does not support the subscription method of GraphQL, the Subscribable Schema and subscription will be introduced later.

Enum

Enum type is supported like a Python enum class.

from pygraphy import Enum


class Episode(pygraphy.Enum):
    NEWHOPE = 4
    EMPIRE = 5
    JEDI = 6


print(Episode)
'''
"""
An enumeration.
"""
enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
'''

Input

The argument of resolver fields can be Input type.

class GeoInput(pygraphy.Input):
    lat: float
    lng: float

    @property
    def latlng(self):
        return "({},{})".format(self.lat, self.lng)


class Address(pygraphy.Object):
    latlng: str


class Query(pygraphy.Query):

    @pygraphy.field
    def address(self, geo: GeoInput) -> Address:
        return Address(latlng=geo.latlng)


class Schema(pygraphy.Schema):
    query: Optional[Query]

Input type can not be the return type of a resolver.

Interface

An Interface type can be inherited to an Object type like a mixin class, all fields inside an Interface will be injected into each Object subclass.

from typing import Optional, List
from pygraphy import Interface, Object, field


class Character(Interface):
    """
    Character interface contains human and droid
    """
    id: str
    name: str

    @field
    def friends(self) -> Optional[List['Character']]:
        return None


class Human(Object, Character):
    """
    Human object
    """
    home_planet: str


class Droid(Object, Character):
    """
    Droid object
    """
    primary_function: str


print(Human)
'''
"""
Human object
"""
type Human implements Character {
  id: String!
  name: String!
  homePlanet: String!
  friends: [Character!]
}
'''

Union

Union type also be supported in Pygraphy.

import pygraphy
from typing import Optional, List


class Foo(pygraphy.Object):
    a: str


class Bar(pygraphy.Object):
    b: int


class FooBar(pygraphy.Union):
    members = (Foo, Bar)


class Query(pygraphy.Object):

    @pygraphy.field
    def get_foobar(self) -> List[FooBar]:
        return [Foo(a='test') for _ in range(5)]


print(FooBar)
'''
union FooBar =
 | Foo
 | Bar
'''