Python Fastapi Graphql Integration Tutorial

Written by

in

FastAPI has quickly become a favorite among Python developers for building high‑performance APIs, thanks to its async capabilities, automatic documentation, and developer‑friendly design. But what if you need the flexibility of GraphQL alongside FastAPI’s speed? In this tutorial, we’ll walk through a step‑by‑step integration of GraphQL into a FastAPI project, covering everything from setting up the environment to defining schemas, resolvers, and testing your endpoint. By the end, you’ll have a production‑ready FastAPI app that serves GraphQL queries and mutations, ready to boost your API’s efficiency and developer experience.

Why Combine FastAPI with GraphQL?

Before diving into code, let’s explore the key benefits of pairing FastAPI with GraphQL:

  • Selective Data Retrieval: Clients request exactly the fields they need, reducing over‑fetching and under‑fetching.
  • Single Endpoint: One flexible endpoint replaces multiple REST routes, simplifying versioning and maintenance.
  • Strong Type System: GraphQL’s schema definition provides clear contracts, while FastAPI’s Pydantic models ensure data validation.
  • Async‑First Performance: FastAPI’s async support pairs perfectly with GraphQL resolvers that may call async database drivers.

Prerequisites

Make sure you have the following before starting:

  1. Python 3.9+ installed.
  2. Basic knowledge of FastAPI and GraphQL concepts.
  3. A virtual environment (venv or Conda) to keep dependencies isolated.

Step 1: Set Up the Project Structure

First, create a clean directory layout. This structure keeps your code organized and scales well as the project grows.

fastapi-graphql-demo/
├─ app/
│  ├─ __init__.py
│  ├─ main.py
│  ├─ schema.py
│  └─ models.py
├─ requirements.txt
└─ README.md

Step 2: Install Required Packages

We’ll use fastapi, uvicorn for the ASGI server, strawberry-graphql as a modern GraphQL library, and pydantic for data validation.

python -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate
pip install fastapi uvicorn strawberry-graphql[fastapi] pydantic
pip freeze > requirements.txt

Step 3: Define Pydantic Models

In app/models.py, create the data models that will back your GraphQL types. Using Pydantic ensures automatic validation and serialization.

from pydantic import BaseModel
from typing import List, Optional

class Book(BaseModel):
    id: int
    title: str
    author: str
    pages: Optional[int] = None

class Author(BaseModel):
    id: int
    name: str
    books: List[Book] = []

Step 4: Build the GraphQL Schema with Strawberry

Strawberry lets you define GraphQL types using Python dataclasses, which feels natural for FastAPI developers.

import strawberry
from typing import List
from .models import Book as BookModel, Author as AuthorModel

@strawberry.type
class Book:
    id: int
    title: str
    author: str
    pages: int | None = None

@strawberry.type
class Author:
    id: int
    name: str
    books: List[Book]

# In‑memory data store for demo purposes
books_db = [
    BookModel(id=1, title="FastAPI in Action", author="John Doe", pages=320),
    BookModel(id=2, title="GraphQL Essentials", author="Jane Smith", pages=210),
]

authors_db = [
    AuthorModel(id=1, name="John Doe", books=[books_db[0]]),
    AuthorModel(id=2, name="Jane Smith", books=[books_db[1]]),
]

@strawberry.type
class Query:
    @strawberry.field
    def books(self) -> List[Book]:
        return [Book(**b.dict()) for b in books_db]

    @strawberry.field
    def authors(self) -> List[Author]:
        return [Author(id=a.id, name=a.name, books=[Book(**b.dict()) for b in a.books]) for a in authors_db]

@strawberry.type
class Mutation:
    @strawberry.mutation
    def add_book(self, title: str, author: str, pages: int | None = None) -> Book:
        new_id = max(b.id for b in books_db) + 1
        new_book = BookModel(id=new_id, title=title, author=author, pages=pages)
        books_db.append(new_book)
        # Update author relationship
        for a in authors_db:
            if a.name == author:
                a.books.append(new_book)
                break
        else:
            authors_db.append(AuthorModel(id=len(authors_db)+1, name=author, books=[new_book]))
        return Book(**new_book.dict())

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

Step 5: Wire GraphQL into FastAPI

FastAPI’s APIRouter makes it trivial to mount the Strawberry GraphQL view.

# app/main.py
import strawberry
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
from .schema import schema

app = FastAPI(
    title="FastAPI + GraphQL Demo",
    description="A simple tutorial showing how to integrate GraphQL with FastAPI using Strawberry.",
    version="1.0.0",
)

graphql_app = GraphQLRouter(schema, graphiql=True)  # Enable GraphiQL UI for testing
app.include_router(graphql_app, prefix="/graphql")

# Optional: Add a health‑check endpoint
@app.get("/health")
async def health_check():
    return {"status": "ok"}

Step 6: Run the Application

Start the server with uvicorn. The GraphQL playground will be available at http://localhost:8000/graphql.

uvicorn app.main:app --reload

Testing a Query

Open the GraphiQL UI and run:

query {
  books {
    id
    title
    author
    pages
  }
}

You should see a JSON response containing the two sample books.

Testing a Mutation

Add a new book using the following mutation:

mutation {
  addBook(title: "Async Python", author: "Alice", pages: 400) {
    id
    title
    author
    pages
  }
}

The new entry will be persisted in the in‑memory list, and subsequent queries will return it.

Step 7: Adding Database Integration (Optional)

For production use, replace the in‑memory lists with an async ORM such as SQLModel or SQLAlchemy 2.0. Below is a quick outline using SQLModel:

# Install extra dependency
pip install sqlmodel[async]

# models.py (extended)
from sqlmodel import SQLModel, Field, Relationship
from typing import List, Optional

class Book(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    title: str
    author: str
    pages: Optional[int] = None
    author_id: Optional[int] = Field(default=None, foreign_key="author.id")

class Author(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    books: List[Book] = Relationship(back_populates="author")

In your resolvers, use await session.exec(...) to fetch data asynchronously. The rest of the GraphQL schema remains unchanged, demonstrating how Strawberry cleanly separates schema definition from data fetching logic.

Best Practices for FastAPI‑GraphQL Projects

  • Schema First Design: Draft your GraphQL schema before writing resolvers; this ensures a clear contract.
  • Use Data Loaders: Prevent the N+1 query problem by batching database calls with strawberry.dataloader or external libraries.
  • Enable CORS: If your frontend runs on a different domain, add fastapi.middleware.cors.CORSMiddleware to allow cross‑origin requests.
  • Secure the Endpoint: Protect the GraphQL route with OAuth2, API keys, or JWT authentication using FastAPI’s dependency injection system.
  • Version Your API: Although GraphQL encourages a single evolving endpoint, you can still version the underlying FastAPI app (e.g., /v1/graphql) for major breaking changes.

Deploying to Production

When you’re ready to go live, consider the following deployment checklist:

  1. Use an ASGI server like uvicorn or hypercorn behind a reverse proxy (NGINX or Traefik).
  2. Enable HTTP/2 and TLS for secure, high‑throughput connections.
  3. Configure logging and monitoring (e.g., structlog, Prometheus, Grafana).
  4. Set up automated tests for both GraphQL queries and FastAPI routes using pytest and httpx.
  5. Scale horizontally with containers (Docker) or serverless platforms that support ASGI.

Conclusion

Integrating GraphQL with FastAPI gives you the best of both worlds: the speed and

Comments

Leave a Reply

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