Getting Started

Installation

pip install strawberry-chemist uvicorn aiosqlite

Supported Python versions: 3.11 through 3.14.

Context contract

In practice, your GraphQL context should provide an async get_session() context manager that yields a SQLAlchemy AsyncSession.

Your own root resolvers will usually use it to load ORM rows, and Chemist-managed fields such as sc.relationship(...), sc.connection(...), and node lookup helpers use that same contract.

At runtime, sc.extensions() also attaches request-local dataloaders and selection caches onto that same context object. That is usually invisible to application code, but it is still part of the execution contract.

Minimal setup

This is a single-file minimal app that serves a Chemist-backed GraphQL schema over ASGI and resolves @sc.type(model=...) instances from real SQLAlchemy queries:

from __future__ import annotations

import asyncio
from contextlib import asynccontextmanager
from typing import Any

import strawberry
import strawberry_chemist as sc
from sqlalchemy import Integer, String, select
from sqlalchemy.ext.asyncio import (
    AsyncSession,
    async_sessionmaker,
    create_async_engine,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from strawberry.asgi import GraphQL

DATABASE_URL = "sqlite+aiosqlite:///./app.db"


class Base(DeclarativeBase):
    pass


class BookModel(Base):
    __tablename__ = "books"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    title: Mapped[str] = mapped_column(String(200))

engine = create_async_engine(DATABASE_URL)
session_factory = async_sessionmaker(engine, expire_on_commit=False)


class AppContext:
    def __init__(self, session_factory: async_sessionmaker[AsyncSession]):
        self._session_factory = session_factory

    @asynccontextmanager
    async def get_session(self):
        async with self._session_factory() as session:
            yield session


def build_context(session_factory: async_sessionmaker[AsyncSession]) -> AppContext:
    return AppContext(session_factory)


@sc.type(model=BookModel)
class Book:
    title: str


@strawberry.type
class Query:
    @strawberry.field
    async def books(
        self,
        info: strawberry.Info[AppContext, None],
    ) -> list[Book]:
        async with info.context.get_session() as session:
            result = await session.scalars(
                select(BookModel).order_by(BookModel.title.asc())
            )
            return list(result)


def build_schema() -> strawberry.Schema:
    return strawberry.Schema(query=Query, extensions=sc.extensions())


class ChemistGraphQL(GraphQL):
    def __init__(
        self,
        schema: strawberry.Schema,
        *,
        session_factory: async_sessionmaker[AsyncSession],
    ) -> None:
        super().__init__(schema)
        self._session_factory = session_factory

    async def get_context(self, request: Any, response: Any) -> AppContext:
        del request, response
        return build_context(self._session_factory)


async def prepare_database() -> None:
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    async with session_factory() as session:
        has_books = await session.scalar(select(BookModel.id).limit(1))
        if has_books is None:
            session.add(BookModel(title="The Hobbit"))
            await session.commit()


app = ChemistGraphQL(build_schema(), session_factory=session_factory)


if __name__ == "__main__":
    import uvicorn

    asyncio.run(prepare_database())
    uvicorn.run(app, host="127.0.0.1", port=8000)

Run it:

python app.py

Then open http://127.0.0.1:8000 and run:

query {
  books {
    title
  }
}

Expected result:

{
  "data": {
    "books": [
      {
        "title": "The Hobbit"
      }
    ]
  }
}

Example projects

The repository ships numbered examples that double as public contract tests. You can inspect them directly or spin one up locally for reference:

cd examples/03_connections_filters_and_ordering
make test
make schema
make serve PORT=8000