Looking to supercharge your Python FastAPI applications with the flexibility of GraphQL? In this step‑by‑step tutorial, you’ll learn how to integrate GraphQL into a FastAPI project, define schemas, handle queries and mutations, and deploy a production‑ready API. Whether you’re a seasoned Python developer or just getting started with FastAPI, this guide provides clear examples, best practices, and SEO‑friendly tips to help your content rank high and your code run smoothly.
Why Combine FastAPI and GraphQL?
FastAPI is renowned for its speed, async support, and automatic OpenAPI documentation. GraphQL, on the other hand, offers clients the power to request exactly the data they need, reducing over‑fetching and under‑fetching issues common with REST. By marrying the two, you get:
- High performance: FastAPI’s async engine pairs perfectly with GraphQL resolvers.
- Fine‑grained data fetching: Clients specify fields, leading to smaller payloads.
- Strong typing: Both FastAPI and GraphQL rely on Python type hints, improving IDE support.
- Unified documentation: FastAPI can serve GraphQL Playground alongside its Swagger UI.
Prerequisites
Before diving in, make sure you have the following installed on your development machine:
python >= 3.9
pip
virtualenv (optional but recommended)
You’ll also need basic familiarity with:
- Python functions and type hints
- FastAPI routing
- Fundamentals of GraphQL (queries, mutations, schemas)
Project Setup
1. Create a virtual environment
python -m venv venv
source venv/bin/activate # On Windows use: venv\Scripts\activate
2. Install required packages
We’ll use fastapi, uvicorn for the ASGI server, and strawberry-graphql as the GraphQL library because it integrates seamlessly with FastAPI.
pip install fastapi uvicorn strawberry-graphql[fastapi] python-multipart
3. Project structure
Keep the project tidy with a clear folder layout:
my_fastapi_graphql/
│
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── schema.py
│ └── models.py
└── requirements.txt
Defining the GraphQL Schema with Strawberry
Strawberry uses Python dataclasses to define GraphQL types, making the code readable and type‑safe.
models.py – Simple data models
from dataclasses import dataclass
from typing import List
@dataclass
class Book:
id: int
title: str
author: str
pages: int
# In‑memory "database"
books_db: List[Book] = [
Book(id=1, title="FastAPI for Beginners", author="Alice", pages=250),
Book(id=2, title="GraphQL Essentials", author="Bob", pages=300),
]
schema.py – GraphQL types, queries, and mutations
import strawberry
from typing import List, Optional
from .models import Book, books_db
@strawberry.type
class BookType:
id: int
title: str
author: str
pages: int
@strawberry.type
class Query:
@strawberry.field
def books(self, info) -> List[BookType]:
"""Return all books."""
return books_db
@strawberry.field
def book(self, info, id: int) -> Optional[BookType]:
"""Find a book by its ID."""
for book in books_db:
if book.id == id:
return book
return None
@strawberry.type
class Mutation:
@strawberry.mutation
def add_book(self, info, title: str, author: str, pages: int) -> BookType:
"""Create a new book and add it to the in‑memory store."""
new_id = max(b.id for b in books_db) + 1 if books_db else 1
new_book = Book(id=new_id, title=title, author=author, pages=pages)
books_db.append(new_book)
return new_book
@strawberry.mutation
def delete_book(self, info, id: int) -> bool:
"""Delete a book by ID. Returns True if successful."""
global books_db
original_len = len(books_db)
books_db = [b for b in books_db if b.id != id]
return len(books_db) < original_len
schema = strawberry.Schema(query=Query, mutation=Mutation)
Connecting Strawberry GraphQL to FastAPI
main.py – FastAPI application entry point
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
from .schema import schema
app = FastAPI(
title="FastAPI + GraphQL Demo",
description="A tutorial project showing how to integrate Strawberry GraphQL with FastAPI.",
version="1.0.0",
)
# Mount the GraphQL endpoint at /graphql
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")
# Optional: a simple health‑check endpoint
@app.get("/health")
async def health_check():
return {"status": "ok"}
Running the Application
Start the ASGI server with uvicorn:
uvicorn app.main:app --reload
When the server is up, navigate to http://127.0.0.1:8000/graphql. You’ll see the interactive GraphQL Playground where you can test queries and mutations.
Sample Queries
- Fetch all books:
{{ books { id title author pages } }} - Fetch a single book by ID:
{{ book(id: 1) { title author } }}
Sample Mutation – Adding a Book
mutation {
addBook(title: "Deep Learning with Python", author: "Carol", pages: 420) {
id
title
author
pages
}
}
Advanced Topics
1. Using async resolvers
If your data source is an async database (e.g., SQLModel or asyncpg), define resolvers with async def:
@strawberry.field
async def books(self, info) -> List[BookType]:
rows = await database.fetch_all("SELECT * FROM books")
return [BookType(**row) for row in rows]
2. Adding authentication
FastAPI’s dependency injection works with Strawberry. Create a dependency that validates a JWT token, then pass it to resolvers via the info.context object.
from fastapi import Depends, HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
bearer = HTTPBearer()
async def get_current_user(credentials: HTTPAuthorizationCredentials = Security(bearer)):
token = credentials.credentials
# Verify token logic here...
if not token_is_valid(token):
raise HTTPException(status_code=401, detail="Invalid token")
return {"user_id": decode(token)["sub"]}
# In main.py
app = FastAPI()
app.dependency_overrides[get_current_user] = get_current_user
# In schema.py
@strawberry.field
def secret_data(self, info) -> str:
user = info.context["request"].state.user
return f"Hello, user {user['user_id']}!"
3. Serving both REST and GraphQL side‑by‑side
FastAPI allows you to keep existing REST endpoints while adding GraphQL. Simply define regular @app.get routes alongside the GraphQLRouter. This hybrid approach is perfect for gradual migrations.
Testing Your GraphQL API
Automated testing ensures reliability. Use pytest together with httpx to send GraphQL requests to the FastAPI test client.
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_books_query():
query = """
{
books {
id
title
}
}
"""
response = client.post("/graphql", json={"query": query})
assert response.status_code == 200
data = response.json()["data"]
assert "books" in data
assert isinstance(data["books"], list)
Deploying to Production
When you’re ready to go live, consider the following production best practices:
- Use a robust ASGI server:
uvicornwithgunicornworkers (e.g.,gunicorn -k uvicorn.workers.UvicornWorker app.main:app). - Enable HTTPS: Deploy behind a reverse proxy like Nginx or use a cloud provider’s TLS termination.
- Set up CORS: FastAPI’s
CORSMiddlewarecan restrict origins for your GraphQL endpoint.
Leave a Reply