Docs Site | Docs Source | Getting Started | Examples
Explicit GraphQL types. Smart SQLAlchemy loading.
- mapped fields
- computed fields
- scoped relationships
- queryable connections
- filters and ordering
- relay IDs
- node lookup
- selection-aware dataloading
strawberry-chemist helps expose SQLAlchemy models through Strawberry without
turning your GraphQL schema into generated magic.
The package is intentionally explicit. You still write the Strawberry types you want clients to see. That keeps the DTO layer visible, keeps the public contract adaptable, and fits production codebases that care about query shape, permissions, loading behavior, and long-term schema maintenance.
That explicitness does not mean giving up performance. Chemist-managed relationship and connection fields are selection-aware and dataloader-backed, so you can keep explicit DTOs without falling into per-parent N+1 loading.
pip install strawberry-chemistServer-scoped relationship-backed field:
import strawberry_chemist as sc
from strawberry_chemist.gql_context import context_var
@sc.type(model=BookModel)
class Book:
@sc.relationship(
"bookmarks",
where=lambda: BookmarkModel.user_id == context_var.get().current_user_id,
select=["id"],
)
def is_bookmarked(self, bookmarks: list[BookmarkModel]) -> bool:
return bool(bookmarks)Note: current_user_id is application data; the package does not ship auth.
Computed field from selected columns:
import strawberry_chemist as sc
@sc.type(model=BookModel)
class Book:
@sc.field(select=["title", "isbn"])
def title_with_isbn(self, title: str, isbn: str) -> str:
return f"{title} ({isbn})"Queryable relationship-backed connection:
import strawberry_chemist as sc
@sc.type(model=AuthorModel)
class Author:
books: sc.Connection[Book] = sc.connection(
source="books",
filter=BookFilter,
order=BookOrder,
pagination=sc.CursorPagination(max_limit=20),
)Your GraphQL context must provide a get_session() async context manager that
returns a SQLAlchemy AsyncSession.
If loader session churn matters for a heavy query, call
sc.configure(use_single_loader_session=True) before schema creation.
Chemist-managed loaders will reuse one request-local AsyncSession for their
internal work; your own resolvers still control session usage through
info.context.get_session(). During operation teardown, Chemist drains
in-flight shared-session loader work before closing that session, and any
late-arriving internal loads fall back to a fresh short-lived session.
The public docs live in docs/. The published site is meruslan.github.io/strawberry-chemist and is built with MkDocs.
Useful entrypoints:
Serve the docs locally:
uv sync --group dev
uv run mkdocs serveBuild the docs locally:
uv run mkdocs build --strictEach numbered example under examples/ is self-contained. The root
example-* commands below just delegate into that example's own Makefile.
Each example is intentionally split into:
db.pyfor SQLAlchemy models and seeded database setupschema.pyforAppContext, GraphQL types, andbuild_schema()app.pyfor the tiny runtime CLI that prints SDL or serves the schema
Serve a seeded sample schema locally:
make example-serve EXAMPLE=03_connections_filters_and_ordering PORT=8000The contract examples tests under examples/ can run in two modes.
Against the current checkout:
make example-test EXAMPLE=03_connections_filters_and_orderingAgainst the pinned published package:
make example-test-published EXAMPLE=03_connections_filters_and_orderingPrint a sample schema:
make example-schema EXAMPLE=03_connections_filters_and_orderingRun the same example directly from its own directory:
cd examples/03_connections_filters_and_ordering
make test
make schema
make serve PORT=8000If you want to force published-mode testing against a locally built
distribution, point the published-mode flow at a build output directory:
uv run python -m build --outdir /tmp/strawberry-chemist-dist
STRAWBERRY_CHEMIST_FIND_LINKS=/tmp/strawberry-chemist-dist \
make example-test-published EXAMPLE=03_connections_filters_and_ordering- Types and fields:
@sc.type,@sc.node,sc.attr,@sc.field - Relationships:
sc.relationship(...) - Collections:
sc.connection(...),sc.Connection,sc.OffsetConnection - Pagination:
sc.CursorPagination,sc.OffsetPagination,sc.PaginationPolicy - Filters:
@sc.filter,sc.FilterSet,sc.filter_field,sc.manual_filter - Ordering:
@sc.order,sc.order_field,sc.manual_order - Relay:
@sc.node,sc.node_field(),sc.node_lookup(...),sc.relay.configure(...) - Schema integration:
sc.extensions()
Each part of the surface has a dedicated public docs page and at least one example reference in docs/examples.md.
Run the default non-Postgres test suite with either:
uv run pytest
make testRun formatting and type checks:
uv run pre-commit run --all-files
uv run mypy
make mypy
make checkRelease notes live in CHANGELOG.md.