FastAPI GraphQL Strawberry Fix
In this tutorial, you'll learn about FastAPI GraphQL Strawberry Fix. We cover key concepts, practical examples, and best practices.
The Problem
Your frontend team wants GraphQL flexibility, but your backend is built with FastAPI REST endpoints. Strawberry GraphQL integrates with FastAPI but requires proper schema setup.
Quick Fix
Wrong — no GraphQL integration
# REST endpoints for every query variation
@app.get("/users")
@app.get("/users/{user_id}")
@app.get("/users/{user_id}/posts")
@app.get("/users/{user_id}/posts/{post_id}/comments")
Output: REST endpoints multiply for each new query pattern. Over-fetching and under-fetching are common.
Correct — Strawberry GraphQL with FastAPI
import strawberry
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
@strawberry.type
class User:
id: int
name: str
email: str
@strawberry.type
class Query:
@strawberry.field
async def user(self, id: int) -> User:
user = await fetch_user(id)
return User(id=user.id, name=user.name, email=user.email)
@strawberry.field
async def users(self) -> List[User]:
users = await fetch_all_users()
return [User(id=u.id, name=u.name, email=u.email) for u in users]
schema = strawberry.Schema(query=Query)
graphql_app = GraphQLRouter(schema)
app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")
Output: Single /<a href="/apis/graphql/">graphql</a> endpoint. Clients query exactly the fields they need.
Mutations
@strawberry.type
class Mutation:
@strawberry.mutation
async def create_user(self, name: str, email: str) -> User:
user = await create_user_in_db(name, email)
return User(id=user.id, name=user.name, email=user.email)
@strawberry.mutation
async def delete_user(self, id: int) -> bool:
await delete_user_from_db(id)
return True
schema = strawberry.Schema(query=Query, mutation=Mutation)
Authentication with Strawberry
from fastapi import Depends
from strawberry.fastapi import BaseContext
class CustomContext(BaseContext):
def __init__(self, user):
self.user = user
async def get_context(user=Depends(get_current_user)):
return CustomContext(user=user)
graphql_app = GraphQLRouter(schema, context_getter=get_context)
@strawberry.type
class Query:
@strawberry.field
async def me(self, info: strawberry.Info) -> User:
return info.context.user
DataLoader for N+1 prevention
from strawberry.dataloader import DataLoader
async def load_users(ids: List[int]) -> List[User]:
users = await fetch_users_by_ids(ids)
return [users[i] for i in ids]
class CustomContext(BaseContext):
def __init__(self):
self.user_loader = DataLoader(load_users)
@strawberry.type
class Query:
@strawberry.field
async def user(self, id: int, info: strawberry.Info) -> User:
return await info.context.user_loader.load(id)
Enums and inputs
from enum import Enum
import strawberry
@strawberry.enum
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
GUEST = "guest"
@strawberry.input
class UserFilter:
role: UserRole | None = None
is_active: bool | None = None
Prevention
- Use Strawberry's DataLoader to batch database queries and prevent N+1.
- Define context getter for authentication and database sessions.
- Use
strawberry.inputfor complex mutation arguments.
Common Mistakes with graphql strawberry
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
- Misunderstanding that
Stringis[Char]with poor performance for large text operations - Using
foldlinstead offoldl'causing stack overflow on large lists
These mistakes appear frequently in real-world FASTAPI code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro