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.
Strawberry is a code-first library: Python type annotations become the GraphQL schema. Install it with:
pip install "strawberry-graphql[fastapi]"| GraphQL | Strawberry / Python |
|---|---|
type Book | @strawberry.type class Book |
input BookInput | @strawberry.input class BookInput |
ID! | strawberry.ID |
nullable String | str | 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
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:
@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)