GraphQL and Strawberry

In REST the endpoint decides the response shape. In GraphQL there is one endpoint, and the client lists the fields it needs. That removes unused fields and extra requests, and the typed schema documents itself. The price is harder caching, file uploads and rate limiting.

A request through Strawberry
A request through Strawberry

Strawberry is a code-first library: Python type annotations become the GraphQL schema. Install it with:

bash
pip install "strawberry-graphql[fastapi]"
GraphQLStrawberry / Python
type Book@strawberry.type class Book
input BookInput@strawberry.input class BookInput
ID!strawberry.ID
nullable Stringstr | None
[Book!]!list[Book]
Query field@strawberry.field
Mutation field@strawberry.mutation
Subscription field@strawberry.subscription + AsyncGenerator

💡 You do not have to replace REST. A GraphQL router can live next to your existing FastAPI endpoints.

Types and queries

python
import strawberry

@strawberry.type
class Book:
    id: strawberry.ID
    title: str
    year: int

BOOKS = [
    Book(id=strawberry.ID("1"), title="Notes", year=2020),
    Book(id=strawberry.ID("2"), title="Kernel", year=2019),
]

@strawberry.type
class Query:
    @strawberry.field
    def books(self) -> list[Book]:
        return BOOKS

schema = strawberry.Schema(query=Query)
result = schema.execute_sync("{ books { title year } }")
print(result.data)
# {'books': [{'title': 'Notes', 'year': 2020}, {'title': 'Kernel', 'year': 2019}]}

execute_sync runs a query without a server, which is convenient for tests. Resolver arguments become GraphQL arguments, and | None makes a result optional:

python
@strawberry.type
class Query:
    @strawberry.field
    def book(self, id: strawberry.ID) -> Book | None:
        return next((b for b in BOOKS if b.id == id), None)