Skip to main content
Version: Next

Step-by-Step example

This example shows how ariadne-codegen can take a GraphQL schema and a set of queries, generate a fully typed Python client, and produce ready-to-use models for queries, mutations, and subscriptions.

Schema file​

We start with a GraphQL schema that defines queries, mutations, and subscriptions. This schema includes User types, input objects for creating users and setting preferences, and a Color enum. It also shows how to use default values in input objects.

schema {
query: Query
mutation: Mutation
subscription: Subscription
}

type Query {
users(country: String): [User!]!
}

type Mutation {
userCreate(userData: UserCreateInput!): User
userPreferences(data: UserPreferencesInput): Boolean!
fileUpload(file: Upload!): Boolean!
}

type Subscription {
usersCounter: Int!
}

scalar Upload

input UserCreateInput {
firstName: String
lastName: String
email: String!
favouriteColor: Color
location: LocationInput
}

input LocationInput {
city: String
country: String
}

type User {
id: ID!
firstName: String
lastName: String
email: String!
favouriteColor: Color
location: Location
}

type Location {
city: String
country: String
}

enum Color {
BLACK
WHITE
RED
GREEN
BLUE
YELLOW
}

input UserPreferencesInput {
luckyNumber: Int = 7
favouriteWord: String = "word"
colorOpacity: Float = 1.0
excludedTags: [String!] = ["offtop", "tag123"]
notificationsPreferences: NotificationsPreferencesInput! = {
receiveMails: true
receivePushNotifications: true
receiveSms: false
title: "Mr"
}
}

input NotificationsPreferencesInput {
receiveMails: Boolean!
receivePushNotifications: Boolean!
receiveSms: Boolean!
title: String!
}

Queries/mutations/subscriptions file​

Next we define the operations we want to use: a mutation to create a user, queries to fetch all users or filter them by country, a subscription to count users in real time, and a mutation to upload a file. Notice that we also define fragments (BasicUser and UserPersonalData) to reuse fields across queries.

mutation CreateUser($userData: UserCreateInput!) {
userCreate(userData: $userData) {
id
}
}

query ListAllUsers {
users {
id
firstName
lastName
email
location {
country
}
}
}

query ListUsersByCountry($country: String) {
users(country: $country) {
...BasicUser
...UserPersonalData
favouriteColor
}
}

fragment BasicUser on User {
id
email
}

fragment UserPersonalData on User {
firstName
lastName
}

subscription GetUsersCounter {
usersCounter
}

mutation uploadFile($file: Upload!) {
fileUpload(file: $file)
}

Running​

To generate Python code, add a [tool.ariadne-codegen] section to your pyproject.toml with paths to the schema and queries files.

[tool.ariadne-codegen]
schema_path = "schema.graphql"
queries_path = "queries.graphql"

Run the command:

$ ariadne-codegen

This generates a Python package with a typed client and models.

Result​

Generated files​

The generated package includes a client, base classes, type definitions, and models for each query, mutation, and subscription:

graphql_client/
__init__.py
async_base_client.py
base_model.py
client.py
create_user.py
enums.py
exceptions.py
fragments.py
get_users_counter.py
input_types.py
list_all_users.py
list_users_by_country.py
scalars.py
upload_file.py

Client class​

The generated client inherits from AsyncBaseClient and has async methods for each query, mutation, and subscription you defined. For example, here is how create_user, list_all_users, list_users_by_country, get_users_counter, and upload_file are implemented:

# graphql_client/client.py
...
class Client(AsyncBaseClient):
async def create_user(...): ...
async def list_all_users(...): ...
async def list_users_by_country(...): ...
async def get_users_counter(...): ...
async def upload_file(...): ...

Each method executes the corresponding GraphQL operation and returns a typed Pydantic model.

Base client​

async_base_client.py contains the networking logic. It is copied from the file path you specify in base_client_file_path and has to define a class named as in base_client_name.

Base model​

base_model.py defines a Pydantic-based BaseModel class. All generated models inherit from this, ensuring validation and serialization work out of the box.

Input types​

Models generated from GraphQL input objects (like UserCreateInput) are strongly typed and used as parameters in client methods.

# graphql_client/input_types.py
class UserCreateInput(BaseModel):
first_name: Optional[str]
last_name: Optional[str]
email: str
favourite_color: Optional[Color]
location: Optional["LocationInput"]

Enums​

GraphQL enums are converted into Python Enum classes. They can be used directly in input types and queries.

# graphql_client/enums.py
class Color(str, Enum):
BLACK = "BLACK"
WHITE = "WHITE"
RED = "RED"
GREEN = "GREEN"
BLUE = "BLUE"
YELLOW = "YELLOW"

Query/mutation/subscription types​

For each query, mutation, or subscription, a model is generated that mirrors the return type. For example, here is the type for the CreateUser mutation:

# graphql_client/create_user.py
class CreateUser(BaseModel):
user_create: Optional["CreateUserUserCreate"]

Other operations like ListAllUsers, ListUsersByCountry, and GetUsersCounter have their own generated models.

Fragments file​

Fragments defined in your operations are generated as reusable Pydantic models that can be inherited by other result models.

# graphql_client/fragments.py
class BasicUser(BaseModel):
id: str
email: str

class UserPersonalData(BaseModel):
first_name: Optional[str]
last_name: Optional[str]

Init file​

The __init__.py file re-exports all generated classes so they can be imported easily.

# graphql_client/__init__.py
from .client import Client
from .create_user import CreateUser
from .enums import Color
from .fragments import BasicUser, UserPersonalData
...

Using the generated client​

Here is how you can use the generated client in your Python application. This shows instantiating the client and calling one of the generated methods.

import asyncio
from graphql_client.client import Client

async def main():
client = Client(url="http://localhost:8000/graphql")
users = await client.list_all_users()
for user in users.users:
print(user.id, user.first_name, user.last_name, user.email)

asyncio.run(main())