GraphQL schema#

graphql2python has the function for generation of pydantic data-model by your GraphQL schema. Below are examples of using these classes for schema from GraphQL documentation https://graphql.org/learn/schema/

Scalar#

The generated scalar looks like this:

# in schema.graphql
scalar DateTime
...

# The `Boolean` scalar type represents `true` or `false`.
Boolean = str


# A Scalar type
# See https://graphql.org/learn/schema/#scalar-types
DateTime = str


# The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most
# often used by GraphQL to represent free-form human-readable text.
String = str

or with comment

"in the format: dd/mm/yyyy"
scalar DateTime
...

# in the format: dd/mm/yyyy
DateTime = str

...

The default python type for generated scalars is str. For change this you can using special option in graphql2python config:

# graphql2python.yaml
schema: ...
output: ...
options:
  scalar_pytypes:
    String: str
    Float: float
    Int: int
    ID: str
    Boolean: bool
    DateTime: datetime
...

# The `Boolean` scalar type represents `true` or `false`.
Boolean = bool


# in the format: dd/mm/yyyy
DateTime = datetime


# The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most
# often used by GraphQL to represent free-form human-readable text.
String = str

...

Enum#

GraphQL Enum objects rendered as Python enum.Enum classes

# in schema.graphql
enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
...

class Episode(enum.Enum):
    """
    An Enum type
    See https://graphql.org/learn/schema/#enumeration-types
    """
    EMPIRE = "EMPIRE"
    JEDI = "JEDI"
    NEWHOPE = "NEWHOPE"

Or with Enum description

"""
This means that wherever we use the type Episode in our schema
we expect it to be exactly one of NEWHOPE, EMPIRE, or JEDI.
"""
enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
...

class Episode(enum.Enum):
    """
    This means that wherever we use the type Episode in our schema
    we expect it to be exactly one of NEWHOPE, EMPIRE, or JEDI.
    """
    EMPIRE = "EMPIRE"
    JEDI = "JEDI"
    NEWHOPE = "NEWHOPE"

Union#

GraphQL Union objects rendered as Python typing.Union

...

"""
Wherever we return a SearchResult type in our schema,
we might get a Human, a Droid, or a Starship.
"""
union SearchResult = Human | Droid | Starship

we have

...

# Wherever we return a SearchResult type in our schema,
# we might get a Human, a Droid, or a Starship.
SearchResult = _t.Union[
    'Droid',
    'Human',
    'Starship',
]

...

Since input.Union cannot contain a single element for such Unions we have the following

...

"""
Wherever we return a SearchResult type in our schema,
we might get a Human, a Droid, or a Starship.
"""
union SearchResult = Human
...

# Wherever we return a SearchResult type in our schema,
# we might get a Human, a Droid, or a Starship.
SearchResult = _t.TypeVar('SearchResult', bound='Human')

...

Object and interfaces#

Rendered Interfaces and Objects are inherited from the main class

class GraphQLBaseModel(BaseModel):
    """Base Model for GraphQL object."""

    class Config:
        allow_population_by_field_name = True
        json_encoders = {
            # custom output conversion for datetime
            datetime: lambda dt: dt.isoformat()
        }
        smart_union = True

So for the following diagram

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

"an interface Character that represents any character in the Star Wars trilogy"
interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}

type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  starships: [Starship]
  totalCredits: Int
}

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

type Starship implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  length: Float
}

we have the following python code

...

class Character(GraphQLBaseModel):
    """
    an interface Character that represents any character in the Star Wars trilogy
    """
    appearsIn: _t.List[_t.Optional['Episode']]
    id: 'ID'
    name: 'String'
    friends: _t.Optional[_t.List[_t.Optional['Character']]] = Field(default_factory=list)
    typename__: _t.Literal["Character"] = Field(default="Character", alias="__typename")


class Droid(
    Character,
):
    """
    An Object type
    See https://graphql.org/learn/schema/#object-types-and-fields
    """
    primaryFunction: _t.Optional['String'] = Field(default=None)
    typename__: _t.Literal["Droid"] = Field(default="Droid", alias="__typename")


class Human(
    Character,
):
    """
    An Object type
    See https://graphql.org/learn/schema/#object-types-and-fields
    """
    starships: _t.Optional[_t.List[_t.Optional['Starship']]] = Field(default_factory=list)
    totalCredits: _t.Optional['Int'] = Field(default=None)
    typename__: _t.Literal["Human"] = Field(default="Human", alias="__typename")


class Starship(
    Character,
):
    """
    An Object type
    See https://graphql.org/learn/schema/#object-types-and-fields
    """
    length: _t.Optional['Float'] = Field(default=None)
    typename__: _t.Literal["Starship"] = Field(default="Starship", alias="__typename")


Character.update_forward_refs()
Droid.update_forward_refs()
Human.update_forward_refs()
Starship.update_forward_refs()

Rename field#

For rename field we can using the following config:

# graphql2python.yaml
schema: ./schema.graphql
output: ./output.py
options:
  fields_setting:
    Character:
      name:
        alias: name
        new_name: character_name
enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}
class Character(GraphQLBaseModel):
    """
    An Interface type
    See https://graphql.org/learn/schema/#interfaces
    """
    appearsIn: _t.List[_t.Optional['Episode']]
    id: 'ID'
    character_name: 'String' = Field(..., alias='name')
    friends: _t.Optional[_t.List[_t.Optional['Character']]] = Field(default_factory=list)
    typename__: _t.Literal["Character"] = Field(default="Character", alias="__typename")

Deprecation field#

graphql2python support only @deprecated directive for fields (because graphql-core supported only this directive https://github.com/graphql-python/graphql-core/issues/162). Example of result with @deprecated:

# schema.graphql
type Country {
  code: ID! @deprecated(reason: "my reason") @requires(fields: "name")
  name: String! @requires(fields: "phone")
  native: String!
  phone: String!
  continent: Continent!
  capital: String
  currency: String
  languages: [Language!]!
  emoji: String!
  emojiU: String!
  states: [State!]!
}
# output.py
class Country(GraphQLBaseModel):
    """
    An Object type
    See https://graphql.org/learn/schema/#object-types-and-fields
    """
    capital: _t.Optional['String'] = Field(default=None)
    code: _t.Optional['ID'] = Field(default=None)  # deprecation_reason: my reason
    continent: _t.Optional['Continent'] = Field(default=None)
    currency: _t.Optional['String'] = Field(default=None)
    emoji: _t.Optional['String'] = Field(default=None)
    emojiU: _t.Optional['String'] = Field(default=None)
    languages: _t.Optional[_t.List['Language']] = Field(default_factory=list)
    name: _t.Optional['String'] = Field(default=None)
    native: _t.Optional['String'] = Field(default=None)
    phone: _t.Optional['String'] = Field(default=None)
    states: _t.Optional[_t.List['State']] = Field(default_factory=list)
    typename__: _t.Literal["Country"] = Field(default="Country", alias="__typename")