Python Fastapi Graphql Integration Tutorial

Written by

in

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: uvicorn with gunicorn workers (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 CORSMiddleware can restrict origins for your GraphQL endpoint.
  • Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *