Relay Support
You can use the official strawberry relay integration directly with django types like this:
import strawberryimport strawberry_djangofrom strawberry_django.relay import DjangoListConnection
class Fruit(models.Model): ...
@strawberry_django.type(Fruit)class FruitType(relay.Node): ...
@strawberry.typeclass Query: # Option 1: Default relay without totalCount # This is the default strawberry relay behaviour. # NOTE: you need to use strawberry_django.connection() - not the default strawberry.relay.connection() fruit: strawberry.relay.ListConnection[FruitType] = strawberry_django.connection()
# Option 2: Strawberry django also comes with DjangoListConnection # this will allow you to get total-count on your query. fruit_with_total_count: DjangoListConnection[ FruitType ] = strawberry_django.connection()
# Option 3: You can manually create resolver by your method manually. @strawberry_django.connection(DjangoListConnection[FruitType]) def fruit_with_custom_resolver(self) -> list[SomeModel]: return Fruit.objects.all()
Behind the scenes this extension is doing the following for you:
- Automatically resolve the
relay.NodeID
field using the modelβs pk - Automatically generate resolves for connections that doesnβt define one. For example,
some_model_conn
andsome_model_conn_with_total_count
will both define a custom resolver automatically that returnsSomeModel.objects.all()
. - Integrate connection resolution with all other features available in this lib. For example, filters , ordering and permissions can be used together with connections defined by strawberry django.
You can also define your own relay.NodeID
field and your resolve, in the same way as
some_model_conn_with_resolver
is doing. In those cases, they will not be overridden.
Tip
If you are only working with types inheriting from relay.Node
and GlobalID
for identifying objects, you might want to set MAP_AUTO_ID_AS_GLOBAL_ID=True
in your strawberry django settings to make sure auto
fields gets
mapped to GlobalID
on types and filters.
Also, this lib exposes a strawberry_django.relay.DjangoListConnection
, which works
the same way as strawberry.relay.ListConnection
does, but also exposes a
totalCount
attribute in the connection.
For more customization options, like changing the pagination algorithm, adding extra fields
to the Connection
/Edge
type, take a look at the
official strawberry relay integration
as those are properly explained there.
Cursor based connections
As an alternative to the default ListConnection
, DjangoCursorConnection
is also available.
It supports pagination through a Django QuerySet
via βtrueβ cursors.
ListConnection
uses slicing to achieve pagination, which can negatively affect performance for huge datasets,
because large page numbers require a large OFFSET
in SQL.
Instead, DjangoCursorConnection
uses range queries such as Q(due_date__gte=...)
for pagination. In combination
with an Index, this makes for more efficient queries.
DjangoCursorConnection
requires a strictly ordered QuerySet
, that is, no two entries in the QuerySet
must be considered equal by its ordering. order_by('due_date')
for example is not strictly ordered, because two
items could have the same due date. DjangoCursorConnection
will automatically resolve such situations by
also ordering by the primary key.
When the order for the connection is configurable by the user (for example via
@strawberry_django.order
) then cursors created by DjangoCursorConnection
will not be compatible
between different orders.
The drawback of cursor based pagination is that users cannot jump to a particular page immediately. Therefor cursor based pagination is better suited for special use-cases like an infinitely scrollable list.
Otherwise DjangoCursorConnection
behaves like other connection classes:
@strawberry.typeclass Query: fruit: DjangoCursorConnection[FruitType] = strawberry_django.connection()
@strawberry_django.connection(DjangoCursorConnection[FruitType]) def fruit_with_custom_resolver(self) -> list[Fruit]: return Fruit.objects.all()