Strawberry with FastAPI

This section documents the current GraphQL specification (October 2021), plus the working draft for @defer/@stream/@oneOf, and the GraphQL-over-HTTP specification, as published at graphql.org and spec.graphql.org — which are the references these pages are written and verified against — and, for the integration pages, against Spring for GraphQL (2.0.x), Strawberry, Ariadne, and the client docs for Apollo Client / urql / Relay. No specific patch version is pinned. Some surfaces (Apollo Router/managed federation, graphql-ws internals, the Relay compiler internals, GraalVM native) are linked rather than documented in depth.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as GraphQL tooling iterates quickly.

Strawberry is a code-first, dataclass-driven GraphQL library for Python, and strawberry.fastapi.GraphQLRouter is its official integration for mounting a schema onto a FastAPI application. This page covers defining types and resolvers, mounting the router, threading FastAPI’s own dependency injection into resolver context, the built-in GraphQL IDEs, and subscriptions over WebSocket and Server-Sent Events.

Prerequisites

This page assumes strawberry-graphql[fastapi] is already installed and that a uvicorn development server is available — both covered in Getting started with Python GraphQL. Every example below assumes the following imports are in scope:

import asyncio
import strawberry
from typing import AsyncGenerator
from fastapi import Depends, FastAPI, Request
from strawberry.fastapi import GraphQLRouter

Defining types with @strawberry.type and @strawberry.field

A Strawberry type is a plain Python dataclass decorated with @strawberry.type; each attribute becomes a GraphQL field, with the Python type hint mapped onto the matching GraphQL scalar or object type. A method decorated with @strawberry.field becomes a computed field, resolved on demand rather than stored as data:

@strawberry.type
class Author:
    id: strawberry.ID
    name: str


@strawberry.type
class Book:
    id: strawberry.ID
    title: str
    author: Author

    @strawberry.field
    def display_title(self) -> str:
        return f"{self.title} by {self.author.name}"

display_title above is a sync resolver — a plain method body, no async/await involved — appropriate for a field computed purely from data already in memory. The next section covers when to reach for async def instead. Scalars, enums, interfaces, unions, generics, and input types are covered in depth in Strawberry — schema and features; this page only introduces enough of the type system to build a runnable FastAPI integration.

The Query, Mutation, and Subscription root types

Every Strawberry schema is assembled from up to three root types — Query is mandatory, Mutation and Subscription are added only if the API needs them — each a @strawberry.type whose fields are the schema’s top-level operations:

@strawberry.type
class Query:
    @strawberry.field
    def book(self, id: strawberry.ID) -> Book:
        return repository.get_book(id)


@strawberry.type
class Mutation:
    @strawberry.mutation
    def add_book(self, title: str, author_id: strawberry.ID) -> Book:
        return repository.create_book(title, author_id)


schema = strawberry.Schema(query=Query, mutation=Mutation)

strawberry.Schema(…​) builds the executable schema from these roots; subscription= joins query= and mutation= once a Subscription root type exists (covered later on this page). The Query/Mutation split mirrors the language-agnostic model in Queries \& fields and Mutations — Strawberry does not change GraphQL’s read/write semantics, only how the roots are declared in Python.

Sync and async resolvers

A @strawberry.field-decorated method can be either a plain def or an async def; Strawberry’s execution engine awaits whichever resolvers declare themselves async and calls the rest synchronously, so the two styles mix freely across a single schema. Reach for async def whenever a resolver performs I/O — a database query, an HTTP call to another service, a DataLoader batch load — so that the ASGI event loop can service other requests (or other fields of the same query, executed concurrently) while the resolver waits:

@strawberry.type
class Query:
    @strawberry.field
    def book(self, id: strawberry.ID) -> Book:            # sync: in-memory lookup
        return repository.get_book(id)

    @strawberry.field
    async def recommended_books(self, info: strawberry.Info) -> list[Book]:
        async with info.context["db_session"] as session:  # async: real I/O
            return await session.fetch_recommended_books()

Mixing a slow synchronous resolver into an otherwise async request path blocks the event loop for every other concurrent request, so a resolver doing blocking I/O (a synchronous database driver, a blocking HTTP client) should either be rewritten against an async driver or offloaded with asyncio.to_thread rather than left as a plain def. Execution \& resolvers covers how the execution engine schedules and awaits resolvers language-agnostically; this section only covers the Python sync/async split on top of it.

Mounting a GraphQLRouter on a FastAPI app

strawberry.fastapi.GraphQLRouter wraps a strawberry.Schema as a FastAPI APIRouter, handling the POST /graphql query/mutation endpoint, the GraphiQL IDE on GET, and (as covered further down) the WebSocket upgrade for subscriptions, all from one router mounted at a single prefix:

app = FastAPI()

graphql_app = GraphQLRouter(schema)

app.include_router(graphql_app, prefix="/graphql")

Because GraphQLRouter is a regular APIRouter, it composes with the rest of a FastAPI application exactly like any other router — other REST routes, middleware, and startup/shutdown events on the same app are unaffected, and nothing about mounting it requires the rest of the application to be GraphQL-aware. See Strawberry — FastAPI for the full integration reference, including the ASGI-level (non-FastAPI) mounting option for applications that only need the router’s underlying ASGI app.

The context_getter: injecting FastAPI dependencies

Every resolver receives a strawberry.Info argument whose .context attribute is a place for the transport layer to hand resolvers whatever request-scoped state they need — the incoming request, a database session, the authenticated user. GraphQLRouter’s `context_getter parameter is itself a FastAPI dependency, which means it can declare Depends(…​) parameters of its own and FastAPI resolves them exactly as it would for a normal route handler, before the GraphQL request executes:

async def get_db_session() -> AsyncGenerator[Session, None]:
    async with SessionLocal() as session:
        yield session


async def get_current_user(request: Request) -> User | None:
    return await authenticate(request)


async def get_context(
    db_session: Session = Depends(get_db_session),
    current_user: User | None = Depends(get_current_user),
) -> dict:
    return {"db_session": db_session, "current_user": current_user}


graphql_app = GraphQLRouter(schema, context_getter=get_context)

A resolver then reads info.context["db_session"] or info.context["current_user"] exactly like the recommended_books resolver did earlier. The dictionary shape above is the simplest option; a context_getter can instead return an instance of a class inheriting strawberry.fastapi.BaseContext, giving resolvers attribute access (info.context.current_user) instead of dictionary keys, and a natural place to add helper methods alongside the request-scoped data. Either way, the mechanism is the same: FastAPI’s own dependency injection graph runs once per request, and its results land in info.context for every resolver in that request to share.

GraphiQL and other GraphQL IDEs

GraphQLRouter serves an in-browser GraphQL IDE on GET requests to its mounted path by default, controlled by the graphql_ide parameter:

graphql_app = GraphQLRouter(
    schema,
    graphql_ide="apollo-sandbox",  # "graphiql" (default), "apollo-sandbox", "pathfinder", or None
)

Passing None disables the IDE entirely, which most teams do once an environment moves from local development toward production — the IDE offers autocomplete and schema exploration against introspection, which is convenience during development rather than something a production deployment needs to expose. The three language-agnostic IDEs themselves (GraphiQL, Apollo Sandbox, and their historical predecessor GraphQL Playground) are introduced in Getting started with GraphQL.

Subscriptions over WebSocket and Server-Sent Events

A Subscription root type looks like Query/Mutation except each field is decorated @strawberry.subscription and returns an AsyncGenerator — the resolver body `yield`s a value each time the subscription should push an update to the client, rather than returning a single result:

@strawberry.type
class Subscription:
    @strawberry.subscription
    async def book_added(self) -> AsyncGenerator[Book, None]:
        async for book in book_added_events():
            yield book


schema = strawberry.Schema(query=Query, mutation=Mutation, subscription=Subscription)

GraphQLRouter serves this subscription over WebSocket by default, supporting both the legacy graphql-ws protocol id (implemented by the older subscriptions-transport-ws package, kept for backward compatibility with older clients) and the newer graphql-transport-ws protocol id (implemented by the actively maintained graphql-ws package) that current clients should prefer; both are negotiated automatically from the client’s requested Sec-WebSocket-Protocol header, with no extra configuration needed for the common case. Server-Sent Events is an HTTP-only alternative transport for subscriptions — useful where a WebSocket upgrade is impractical (some proxies, some serverless deployments) — enabled by opting a schema into the SSE protocol explicitly rather than being on by default alongside the WebSocket protocols.

Authentication for a subscription connection is handled differently from a query or mutation, because a WebSocket’s initial connection parameters (not an HTTP header set once per request) are where a client typically passes a token; GraphQLRouter exposes an on_ws_connect hook a server can override to inspect those parameters and raise a ConnectionRejectionError to refuse the connection before any subscription resolver runs. Subscriptions covers the subscription operation type and its WebSocket sub-protocols language-agnostically; see Strawberry — Subscriptions for the full Python reference, including cancellation handling and the ASGI/AIOHTTP/Django integrations beyond FastAPI.

The FastAPI + Strawberry request path

Tying the pieces on this page together, a single GraphQL request over HTTP moves through GraphQLRouter, its context_getter, the schema’s execution engine, and the resolvers defined above, with DataLoader batching (covered in depth on Performance \& N+1) sitting between a resolver and the datastore it ultimately reads from:

flowchart LR A[Client request] --> B[GraphQLRouter] B --> C[context_getter: FastAPI Depends] C --> D[Schema execute] D --> E[Sync/async resolvers] E --> F[DataLoader batch] F --> G[Datastore] E --> H[Response envelope]

This diagram is specific to the FastAPI + Strawberry integration on this page; the language-agnostic parse/validate/execute/respond lifecycle it sits inside of is covered once, as the canonical reference, in Getting started with GraphQL.

Where to go from here

This page covered enough of Strawberry’s type system and FastAPI integration to run a working GraphQL server; three siblings go deeper on adjacent concerns:

  • Strawberry — schema and features covers scalars, enums, interfaces, unions, generics, input types, permission classes, DataLoader in depth, Relay pagination helpers, and schema export/codegen.

  • Ariadne — schema-first covers the alternative, SDL-first way to build a Python GraphQL server, for teams that prefer defining the schema as text rather than as decorated Python classes.

  • Serving over HTTP covers the wire-level request/response contract that GraphQLRouter implements underneath the Python API shown on this page.

This page was generated with the assistance of AI. Verify the exact GraphQLRouter parameter names and behavior — particularly context_getter, graphql_ide, and the subscription protocol options — against Strawberry’s FastAPI integration docs before relying on them in production, since Strawberry’s API surface evolves between releases.