Skip to content

GraphQL Integration¶

Cello provides first-class GraphQL support with decorator-based schema definition, DataLoader for N+1 prevention, and WebSocket-based subscriptions.

Quick Start¶

from cello import App
from cello.graphql import Query, Mutation, Schema, DataLoader, GraphQL

app = App()

@Query
def users(info) -> list:
    return [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]

@Query
def user(info, id: int) -> dict:
    return {"id": id, "name": "Alice"}

@Mutation
def create_user(info, name: str, email: str) -> dict:
    return {"id": 3, "name": name, "email": email}

schema = Schema().query(users).query(user).mutation(create_user).build()
result = await schema.execute("{ users }")

Decorators¶

@Query¶

Marks a function as a GraphQL query resolver.

from cello.graphql import Query

@Query
def books(info) -> list:
    return db.get_all_books()

@Query
def book(info, id: int) -> dict:
    return db.get_book(id)

@Mutation¶

Marks a function as a GraphQL mutation resolver.

from cello.graphql import Mutation

@Mutation
def create_book(info, title: str, author: str) -> dict:
    return db.create_book(title, author)

@Subscription¶

Marks an async generator as a GraphQL subscription.

from cello.graphql import Subscription

@Subscription
async def book_added(info):
    async for event in event_stream("book_added"):
        yield event

DataLoader¶

Prevents N+1 query problems by batching and caching database calls.

from cello.graphql import DataLoader

async def batch_load_authors(ids):
    return [db.get_author(id) for id in ids]

author_loader = DataLoader(batch_fn=batch_load_authors)

# Single load (cached after first call)
author = await author_loader.load(1)

# Batch load
authors = await author_loader.load_many([1, 2, 3])

# Clear cache
author_loader.clear(1)       # Clear single key
author_loader.clear()         # Clear all

Schema Builder¶

Compose queries, mutations, and subscriptions into a schema using the fluent builder API.

from cello.graphql import Schema

schema = (
    Schema()
    .query(users)
    .query(user)
    .mutation(create_user)
    .mutation(update_user)
    .subscription(user_created)
    .build()
)

Class-Based Types¶

You can also register entire classes where each method becomes a resolver:

class QueryType:
    def users(self, info) -> list:
        return db.get_all_users()

    def books(self, info) -> list:
        return db.get_all_books()

schema = Schema().query(QueryType).build()

GraphQL Engine¶

Execute queries directly using the GraphQL engine.

from cello.graphql import GraphQL

engine = GraphQL()
engine.add_query(users)
engine.add_mutation(create_user)

result = await engine.execute("{ users }")
# {"data": {"users": [...]}, "errors": None}

# With variables
result = await engine.execute(
    "mutation($name: String!) { createUser(name: $name) }",
    variables={"name": "Alice"}
)

Configuration¶

Enable GraphQL on your Cello app:

from cello import App, GraphQLConfig

app = App()
app.enable_graphql(GraphQLConfig(
    path="/graphql",
    playground=True,
    introspection=True
))
Option Default Description
path /graphql GraphQL endpoint path
playground True Enable GraphiQL playground
introspection True Enable schema introspection

API Reference¶

Class Description
Query Decorator for query resolvers
Mutation Decorator for mutation resolvers
Subscription Decorator for subscription resolvers
Field GraphQL field definition with optional resolver
DataLoader Batching and caching loader for N+1 prevention
GraphQL Execution engine for running queries
Schema Fluent builder for composing a schema