Skip to content
Go back

Build Graphql Api Strawberry Fastapi Sqlite

Close Up Photo of Programming of Codes
Photo by pexels

Table of contents

Open Table of contents

Setting up the Project

The first thing we need is a clean Python project. For this, we use Poetry, a modern dependency management tool. Poetry creates an isolated environment and handles installation cleanly. you can start by running following command.

poetry new graphql-api
cd graphql-api

This command will guide you to create a new project. After that, we need install the dependencies

poetry add fastapi strawberry-graphql databases sqlalchemy aiosqlite uvicorn

Here is what each package does. FastAPI runs the application. Strawberry adds GraphQL support. Databases gives us async database connections. SQLAlchemy defines our tables and models. aiosqlite is the driver to connect to SQLite in async mode. Finally, uvicorn runs the server.

Once this setup is complete, we are ready to start coding.

Database Setup

The database logic lives inside database.py. Let’s look at the code:

from databases import Database
from sqlalchemy import create_engine, MetaData

DATABASE_URL = "sqlite:///./test.db"

database = Database(DATABASE_URL)
metadata = MetaData()

engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})src/graphql-api/database.py

This file creates the connection to the database. The DATABASE_URL points to a local SQLite file called test.db. The Database object from the databases package gives us an async-friendly way to interact. We also define metadata to hold the structure of our tables. The engine is the actual SQLAlchemy engine used to create tables.

Notice the check_same_thread setting. SQLite normally enforces that one connection belongs to one thread. Since FastAPI works asynchronously with multiple tasks, we disable this check so the connection works smoothly.

This file will be imported later wherever we need to connect to the database.

Defining Models

Now we move to models.py, where we define the actual database tables.

from sqlalchemy import Table, Column, Integer, String, ForeignKey
from .database import metadata

users = Table(
    "users",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String, nullable=False),
)

posts = Table(
    "posts",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("title", String, nullable=False),
    Column("content", String, nullable=False),
    Column("user_id", Integer, ForeignKey("users.id"))
)src/graphql-api/models.py

Here we define two tables: users and posts. The users table has two columns: an id as the primary key and a name that cannot be empty. The posts table stores blog posts. Each post has its own id, a title, some content, and a user_id that links back to the users table. The ForeignKey("users.id") ensures that every post belongs to a valid user.

This is the foundation of our database schema. Users can have many posts, but each post belongs to exactly one user.

Building the FastAPI and GraphQL App

Now comes the main file, main.py. This is where everything connects. Let’s break it down step by step.

Importing and Setting Up

from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
import strawberry
from typing import List

from .database import database, engine, metadata
from .models import users, postssrc/graphql-api/main.py

We bring in FastAPI, Strawberry, typing helpers, and our database and model definitions. This sets the stage for the application.

We then create the tables in SQLite.

metadata.create_all(engine)src/graphql-api/main.py

Defining GraphQL Types

@strawberry.type
class Post:
    id: int
    title: str
    content: str

@strawberry.type
class User:
    id: int
    name: str
    posts: List[Post]src/graphql-api/main.py

In GraphQL, you need to define types that represent the shape of data clients can query. We have two types: Post and User. The User type includes a list of posts, so when a query requests a user, you can also fetch their posts in one request. This is one of the strongest features of GraphQL: nested queries become natural.

Writing Queries

@strawberry.type
class Query:
    @strawberry.field
    async def all_users(self) -> List[User]:
        query = users.select()
        result = await database.fetch_all(query)
        users_list = []
        for row in result:
            post_query = posts.select().where(posts.c.user_id == row["id"])
            user_posts = await database.fetch_all(post_query)
            posts_list = [Post(id=p["id"], title=p["title"], content=p["content"]) for p in user_posts]
            users_list.append(User(id=row["id"], name=row["name"], posts=posts_list))
        return users_list

    @strawberry.field
    async def get_user(self, id: int) -> User | None:
        query = users.select().where(users.c.id == id)
        user_row = await database.fetch_one(query)
        if user_row:
            post_query = posts.select().where(posts.c.user_id == id)
            user_posts = await database.fetch_all(post_query)
            posts_list = [Post(id=p["id"], title=p["title"], content=p["content"]) for p in user_posts]
            return User(id=user_row["id"], name=user_row["name"], posts=posts_list)
        return Nonesrc/graphql-api/main.py

Here we define two queries. The all_users query fetches all users from the database, then for each user fetches their posts, and finally returns a list of users with their posts attached.

The get_user query fetches a single user by ID. If the user exists, it also retrieves their posts. If the user does not exist, it returns None.

Notice how much flexibility GraphQL gives us. With one request, you can get both the user and their posts instead of making separate calls.

Writing Mutations

@strawberry.type
class Mutation:
    @strawberry.mutation
    async def create_user(self, name: str) -> User:
        query = users.insert().values(name=name)
        user_id = await database.execute(query)
        return User(id=user_id, name=name, posts=[])

    @strawberry.mutation
    async def create_post(self, user_id: int, title: str, content: str) -> Post:
        query = posts.insert().values(user_id=user_id, title=title,    content=content)
        post_id = await database.execute(query)
        return Post(id=post_id, title=title, content=content)src/graphql-api/main.py

Mutations are GraphQL’s way of writing data. We define two mutations here.

The create_user mutation inserts a new row into the users table with the given name. It returns a User object with an empty posts list.

The create_post mutation creates a new post for a given user. It inserts the title, content, and the user ID into the posts table and returns the created post.

These two mutations allow clients to grow the database by adding users and posts.

Putting It All Together

Finally, we connect everything into the FastAPI app.

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

app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()src/graphql-api/main.py

We build the schema by combining queries and mutations. The GraphQLRouter turns the schema into a FastAPI router. We then include it under the /graphql path. When the app starts, we connect to the database. When the app shuts down, we disconnect cleanly.

Running the API

With everything ready, run the app using uvicorn.

poetry run uvicorn main:app --reload

Go to http://127.0.0.1:8000/graphql in your browser. You will see a GraphQL playground.

You can run queries this to test your API.

mutation {
  createUser(name: "lakshan") {
    id
    name
  }
}

This creates a user named lakshan. Then you can add a post.

mutation {
  createPost(userId: 1, title: "Hello", content: "My first post") {
    id
    title
    content
  }
}

Finally, fetch everything.

query {
  allUsers {
    id
    name
    posts {
      id
      title
      content
    }
  }
}

The result shows all the users and content they created. All of this happens through one endpoint, yet you control the shape of the response.

What We Built and Next Steps

We have built a complete GraphQL API using Strawberry and FastAPI, backed by SQLite. The API supports creating users, creating posts, and fetching data with nested queries. Along the way, we set up a clean project with Poetry, structured the database, and integrated everything into FastAPI.

There are many ways you can extend this project. You could add authentication so only certain users can create posts. You could implement pagination for the all_users query to handle large datasets. You could also swap SQLite with PostgreSQL or MySQL for production use. If you want more advanced GraphQL features, you can add input types, subscriptions for real-time updates, or custom resolvers.

The key takeaway is that with Strawberry and FastAPI, building a GraphQL API in Python is both simple and powerful. You get strong typing, async performance, and a schema-first design that makes your API easy to use and maintain.


Share this post on:

Previous Post
Adding new posts in AstroPaper theme
Next Post
Customizing AstroPaper theme color schemes