Are you looking to combine the lightning‑fast performance of FastAPI with the flexibility of GraphQL in a Python project? This step‑by‑step tutorial shows you exactly how to integrate GraphQL into a FastAPI application, covering everything from environment setup to advanced schema design. Whether you’re a seasoned backend developer or just starting out, you’ll walk away with a production‑ready codebase that can handle complex queries, mutations, and real‑time subscriptions—all while keeping the simplicity and speed that FastAPI is famous for.
Why Pair FastAPI with GraphQL?
FastAPI has quickly become a favorite among Python developers because it delivers asynchronous performance, automatic OpenAPI documentation, and type‑hint‑driven validation. GraphQL, on the other hand, gives clients the power to request exactly the data they need, reducing over‑fetching and under‑fetching problems common with REST APIs. By marrying the two, you get:
- High‑throughput, non‑blocking request handling.
- Self‑documenting GraphQL schemas that evolve with your code.
- One endpoint for queries, mutations, and subscriptions.
- Seamless integration with modern front‑end frameworks like React, Apollo, and Relay.
Prerequisites
Before diving into the code, make sure you have the following installed:
- Python 3.9 or newer.
- Virtual environment tools (
venvorconda). - Basic knowledge of FastAPI and GraphQL concepts.
We’ll use Strawberry as the GraphQL library because it aligns perfectly with FastAPI’s async nature and type‑hinting approach.
Step 1 – Set Up the Project
Create a fresh directory and initialise a virtual environment:
mkdir fastapi-graphql-demo
cd fastapi-graphql-demo
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
Install FastAPI, Uvicorn (the ASGI server), and Strawberry:
pip install fastapi uvicorn strawberry[fastapi] python-multipart
The python-multipart package is required for file uploads if you decide to extend the tutorial later.
Step 2 – Define a Simple Data Model
For illustration, we’ll model a tiny book store. Create a file named models.py:
from dataclasses import dataclass
from typing import List
@dataclass
class Author:
id: int
name: str
@dataclass
class Book:
id: int
title: str
author_id: int
In a real‑world scenario you would replace these dataclasses with SQLAlchemy models or an ORM of your choice.
Step 3 – Build the GraphQL Schema with Strawberry
Create schema.py and expose both query and mutation types.
import strawberry
from typing import List, Optional
from models import Author, Book
# In‑memory “database”
authors = [
Author(id=1, name="George Orwell"),
Author(id=2, name="J.K. Rowling"),
]
books = [
Book(id=1, title="1984", author_id=1),
Book(id=2, title="Animal Farm", author_id=1),
Book(id=3, title="Harry Potter and the Sorcerer's Stone", author_id=2),
]
@strawberry.type
class AuthorType:
id: int
name: str
@strawberry.type
class BookType:
id: int
title: str
author: AuthorType
@strawberry.field
def author(self) -> AuthorType:
# Resolve the author relationship
return next(a for a in authors if a.id == self.author_id)
@strawberry.type
class Query:
@strawberry.field
def books(self) -> List[BookType]:
return books
@strawberry.field
def author(self, id: int) -> Optional[AuthorType]:
return next((a for a in authors if a.id == id), None)
@strawberry.type
class Mutation:
@strawberry.mutation
def add_book(self, title: str, author_id: int) -> BookType:
new_id = max(b.id for b in books) + 1
new_book = Book(id=new_id, title=title, author_id=author_id)
books.append(new_book)
return new_book
schema = strawberry.Schema(query=Query, mutation=Mutation)
Notice how @strawberry.field and @strawberry.mutation automatically generate the GraphQL schema from Python type hints. This tight coupling is what makes Strawberry a natural fit for FastAPI.
Step 4 – Mount the GraphQL Endpoint in FastAPI
Create the main application file main.py:
from fastapi import FastAPI
import strawberry.fastapi
from schema import schema
app = FastAPI(
title="FastAPI + GraphQL Demo",
description="A minimal example showing how to integrate Strawberry GraphQL with FastAPI.",
version="0.1.0",
)
# Mount the GraphQL endpoint at /graphql
graphql_app = strawberry.fastapi.GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")
# Optional: expose a simple health check
@app.get("/health")
async def health_check() -> dict:
return {"status": "ok"}
Run the server with:
uvicorn main:app --reload
Visit http://127.0.0.1:8000/graphql in your browser. Strawberry automatically provides an interactive GraphiQL UI where you can test queries and mutations.
Step 5 – Test Your First Queries
Open the GraphiQL interface and try the following query to fetch all books with their authors:
query {
books {
id
title
author {
id
name
}
}
}
The response should look similar to:
{
"data": {
"books": [
{
"id": 1,
"title": "1984",
"author": {
"id": 1,
"name": "George Orwell"
}
},
{
"id": 2,
"title": "Animal Farm",
"author": {
"id": 1,
"name": "George Orwell"
}
},
{
"id": 3,
"title": "Harry Potter and the Sorcerer's Stone",
"author": {
"id": 2,
"name": "J.K. Rowling"
}
}
]
}
}
Now add a new book using a mutation:
mutation {
addBook(title: "Harry Potter and the Chamber of Secrets", authorId: 2) {
id
title
author {
name
}
}
}
Refresh the previous query to see the newly inserted record. This demonstrates the full round‑trip of reading and writing data via GraphQL.
Advanced Topics
1️⃣ Adding Authentication
FastAPI’s dependency injection works seamlessly with Strawberry. Create a dependency that extracts a user from a JWT token, then pass it to resolver functions:
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
security = HTTPBearer()
def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security)
) -> str:
try:
payload = jwt.decode(credentials.credentials, "YOUR_SECRET", algorithms=["HS256"])
return payload["sub"]
except jwt.PyJWTError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
)
Then inject the user into a resolver:
@strawberry.type
class Mutation:
@strawberry.mutation
async def add_book(
self,
title: str,
author_id: int,
user: str = Depends(get_current_user)
) -> BookType:
# You could log the user, enforce permissions, etc.
return await add_book_logic(title, author_id, created_by=user)
2️⃣ Using an Async ORM (SQLModel Example)
If you prefer a real database, replace the in‑memory lists with SQLModel (built on SQLAlchemy). The resolver signatures stay the same; only the data‑access layer changes:
from sqlmodel import SQLModel, Field, select
from sqlalchemy.ext.asyncio import AsyncSession
class BookModel(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str
author_id: int
async def get_books(session: AsyncSession) -> List[BookModel]:
result = await session.exec(select(BookModel))
return result.all()
Pass the AsyncSession as a FastAPI dependency and use it inside your Strawberry resolvers.
3️⃣ Enabling Subscriptions for Real‑Time Updates
FastAPI supports WebSockets out of the box, and Strawberry can expose GraphQL subscriptions over them.
Leave a Reply