Connections And Pagination
sc.connection(...) is the collection field entrypoint for both root fields and
relationship-backed collections.
Like relationship fields, Chemist-managed connection fields are selection-aware and dataloader-backed. The point is to let you expose rich root and nested collections without hand-building anti-N+1 plumbing.
Connections are also intentionally flexible. The same API covers:
- root collections
- relationship-backed collections
- server-scoped collections with
where=... - server-owned default ordering with
default_order_by=... - extra child-row loading for computed return-node data via
select=... - parent-aware nested connections with
parent_select=... - filter and order arguments
- flat pagination arguments
- nested
pagination:input objects for compatibility-sensitive schemas
Root connection
@strawberry.type
class Query:
books: sc.Connection[Book] = sc.connection(
filter=BookFilter,
order=BookOrder,
pagination=sc.CursorPagination(max_limit=20),
)
Relationship-backed connection
@sc.type(model=AuthorModel)
class Author:
books: sc.Connection[Book] = sc.connection(
source="books",
filter=BookFilter,
order=BookOrder,
)
Use parent_select= when a nested connection resolver needs parent-row fields
in addition to the paginated related collection.
@sc.connection(
source="books",
source_param_name="loaded_connection",
parent_select=["name"],
)
def books_for_author(
self,
loaded_connection: sc.Connection[BookModel],
) -> sc.Connection[Book]:
if self.name:
return loaded_connection
return loaded_connection
The injected resolver argument is a hidden runtime value, not a GraphQL input. For connections, that runtime value is the loaded connection wrapper whose nodes are still ORM rows.
By default that hidden injected parameter uses the effective source name. Use
source_param_name= when you want a different Python parameter name.
As with relationships, the split is:
select=for extra fields on the child rows when the resolver computes runtime-only return-node dataparent_select=for extra fields on the parent rowwhere=,filter=,order=,default_order_by=, andpagination=for the child collection
parent_select= only applies to relationship-backed connections. Root
connections should not declare it.
Computed return-node data
Use select= when the connection resolver needs child-row columns that clients
may not request directly.
@strawberry.type
class BookPreview(Book):
title_matches_prefix: bool
@sc.connection(
source="books",
select=["title"],
source_param_name="loaded_connection",
)
def books_matching(
self,
loaded_connection: sc.Connection[BookModel],
title_prefix: str,
) -> sc.Connection[BookPreview]:
for edge in loaded_connection.edges:
edge.node.title_matches_prefix = edge.node.title.startswith(title_prefix)
return loaded_connection
That pattern is supported. Plain runtime-only GraphQL fields on the returned
node type stay out of SQL column selection, while select= makes the source
columns needed for the computation available.
Scoped connection
@strawberry.type
class Query:
visible_books: sc.Connection[Book] = sc.connection(
where=lambda: BookModel.visible.is_(True),
)
where= applies server-owned SQLAlchemy predicates before any client-provided
filter or ordering inputs.
Default server ordering
@strawberry.type
class Query:
ranked_books: sc.Connection[Book] = sc.connection(
where=lambda: BookModel.visible.is_(True),
default_order_by=(BookModel.ranking.desc(), BookModel.title.asc()),
)
Use default_order_by= when a connection should have a stable server-owned
sort even when no order= argument is exposed or when the client does not pass
orderBy:.
Pagination policies
Built-in policies:
sc.CursorPagination(...)sc.OffsetPagination(...)
They both implement sc.PaginationPolicy.
If an application wants one package-level default policy, configure it before building the Strawberry schema:
sc.configure(
default_pagination=sc.CursorPagination(default_limit=10, max_limit=20),
)
Then sc.connection() fields without an explicit pagination= value inherit
that policy. Explicit pagination= on a field still wins.
Flat argument style:
sc.connection(pagination=sc.CursorPagination(max_limit=20))
GraphQL:
books(first: 20, after: "...")
Nested argument style:
sc.connection(pagination=sc.CursorPagination(max_limit=20, nested=True))
GraphQL:
books(pagination: { first: 20, after: "..." })
The nested style is especially useful when an existing client contract already
uses a pagination: input object.
Primary examples: