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