GraphQL и Strawberry

В REST форму ответа определяет endpoint. В GraphQL один endpoint, а клиент перечисляет нужные поля. Так пропадают лишние поля и лишние запросы, а типизированная схема описывает сама себя. Цена — более сложные кэширование, загрузка файлов и лимиты запросов.

Запрос через Strawberry
Запрос через Strawberry

Strawberry работает в подходе code-first: аннотации типов Python становятся схемой GraphQL. Установка:

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@strawberry.field
поле Mutation@strawberry.mutation
поле Subscription@strawberry.subscription + AsyncGenerator

💡 Заменять REST не обязательно. Роутер GraphQL может жить рядом с вашими эндпоинтами FastAPI.

Типы и запросы

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 выполняет запрос без сервера, это удобно в тестах. Аргументы резолвера становятся аргументами GraphQL, а | None делает результат необязательным:

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)