Types And Fields

Chemist keeps explicit Strawberry classes at the center of the API.

Direct mapping

When the GraphQL field name matches the SQLAlchemy attribute, just annotate it:

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

Use sc.attr(...) when the GraphQL field should be renamed:

@sc.type(model=BookModel)
class Book:
    published_year: int = sc.attr("year")

Computed fields

Use @sc.field(select=[...]) when the field is computed from parent data:

@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})"

That keeps the DTO explicit and keeps required model data visible at the field definition site. Selected resolver params are hidden from the GraphQL schema. Any other resolver params you declare stay public GraphQL arguments.

If the resolver parameter names should differ from the selected model field paths, pass a mapping instead:

@sc.type(model=BookModel)
class Book:
    @sc.field(select={"title": "book_title", "isbn": "book_isbn"})
    def title_with_isbn(self, book_title: str, book_isbn: str) -> str:
        return f"{book_title} ({book_isbn})"

Node types

Use sc.Node when the type participates in relay/node resolution:

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

Override the default primary-key relay ID only when needed:

@sc.type(model=BookmarkModel)
class Bookmark(sc.Node):
    id = sc.node_id(ids=("user_id", "book_id"))

Primary example: