Getting started
This page builds a working GraphQL API over three SQLAlchemy models, served by Litestar on SQLite. By the end, you'll have a running server and a query you can execute against it. The code lives in a quickstart package with four modules built up over the next steps: models.py, types.py, schema.py, and app.py.
Installation
uv add "strawchemy[asyncio]" "litestar[sqlalchemy,standard]" aiosqlite strawberry-graphqlstrawchemy[asyncio] brings what async sessions need; strawchemy[geo] adds PostGIS support through GeoAlchemy2.
Models
A user writes posts, and posts carry tags through an association table:
from __future__ import annotations
from datetime import datetime
from sqlalchemy import Column, ForeignKey, Table
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
class Base(DeclarativeBase):
pass
post_tag = Table(
"post_tag",
Base.metadata,
Column("post_id", ForeignKey("post.id", ondelete="CASCADE"), primary_key=True),
Column("tag_id", ForeignKey("tag.id", ondelete="CASCADE"), primary_key=True),
)
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
email: Mapped[str]
posts: Mapped[list[Post]] = relationship("Post", back_populates="author")
class Post(Base):
__tablename__ = "post"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
content: Mapped[str]
views: Mapped[int] = mapped_column(default=0)
published_at: Mapped[datetime | None] = mapped_column(default=None)
author_id: Mapped[int | None] = mapped_column(ForeignKey("user.id"), default=None)
author: Mapped[User | None] = relationship("User", back_populates="posts")
tags: Mapped[list[Tag]] = relationship("Tag", secondary=post_tag, back_populates="posts")
class Tag(Base):
__tablename__ = "tag"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
posts: Mapped[list[Post]] = relationship("Post", secondary=post_tag, back_populates="tags")GraphQL mapping
Start with the mapper and the three @strawchemy.type classes:
from strawchemy import Strawchemy, StrawchemyAsyncRepository, StrawchemyConfig
from quickstart.models import Post, Tag, User
strawchemy = Strawchemy(StrawchemyConfig("sqlite", repository_type=StrawchemyAsyncRepository))
@strawchemy.type(User, include="all")
class UserType: ...
@strawchemy.type(Post, include="all", override=True)
class PostType: ...
@strawchemy.type(Tag, include="all", override=True)
class TagType: ...- The mapper's dialect is
sqlite. repository_typemust beStrawchemyAsyncRepositorybecause the session used below is async — the default isStrawchemySyncRepository.PostTypeandTagTypeneedoverride=Truebecause mappingUserTypealready generated types for the relatedPostandTagmodels.
Filtering and sorting
Add filter and order-by inputs for each model:
@strawchemy.filter(User, include="all")
class UserFilter: ...
@strawchemy.filter(Post, include="all", override=True)
class PostFilter: ...
@strawchemy.order(User, include="all")
class UserOrderBy: ...
@strawchemy.order(Post, include="all", override=True)
class PostOrderBy: ...These become the filter and orderBy arguments on the GraphQL fields.
Building the schema
import strawberry
from quickstart.types import PostFilter, PostOrderBy, PostType, UserFilter, UserOrderBy, UserType, strawchemy
@strawberry.type
class Query:
users: list[UserType] = strawchemy.field(filter_input=UserFilter, order_by_input=UserOrderBy, pagination=True)
posts: list[PostType] = strawchemy.field(filter_input=PostFilter, order_by_input=PostOrderBy, pagination=True)
schema = strawberry.Schema(query=Query)Serving the API
Wire the schema into a Litestar app:
from __future__ import annotations
from typing import TYPE_CHECKING
from litestar import Litestar
from litestar.plugins.sqlalchemy import SQLAlchemyAsyncConfig, SQLAlchemyPlugin
from strawberry.litestar import BaseContext, make_graphql_controller
from quickstart.models import Base
from quickstart.schema import schema
if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession
config = SQLAlchemyAsyncConfig(
connection_string="sqlite+aiosqlite:///quickstart.sqlite",
create_all=True,
metadata=Base.metadata,
)
class GraphQLContext(BaseContext):
session: AsyncSession
async def context_getter(db_session: AsyncSession) -> GraphQLContext:
return GraphQLContext(db_session)
def create_app() -> Litestar:
return Litestar(
plugins=[SQLAlchemyPlugin(config=config)],
route_handlers=[make_graphql_controller(schema, context_getter=context_getter)],
)session_getter reads the session from the context context_getter returns. Run the server:
uv run litestar --app quickstart.app:create_app run --reloadOpen http://127.0.0.1:8000 to reach GraphiQL.
TIP
The complete project is in examples/quickstart in the repository.
Querying
{
users(limit: 10, filter: { name: { contains: "Al" } }, orderBy: { name: ASC }) {
id
name
posts {
title
views
}
}
}The database starts empty, so querying it now returns {"data": {"users": []}}. Once a User row named "Alice" exists with a Post titled "Hello", the same query returns:
{
"data": {
"users": [
{
"id": 1,
"name": "Alice",
"posts": [{ "title": "Hello", "views": 3 }]
}
]
}
}Writing data
Add a create input to types.py, then wire it into a new Mutation type in schema.py — add PostCreateInput to its import from quickstart.types:
@strawchemy.create_input(Post, include=["title", "content", "views"])
class PostCreateInput: ...@strawberry.type
class Mutation:
create_post: PostType = strawchemy.create(PostCreateInput)
schema = strawberry.Schema(query=Query, mutation=Mutation) mutation {
createPost(data: { title: "Hello", content: "My first post", views: 0 }) {
id
title
}
}