Example Relay connection #535
-
I created a very basic relay connection type today. It's very verbose and relies on # relay.py
from typing import Tuple, Optional, TypeVar, Generic
from graphql_relay import get_offset_with_default
import strawberry
@strawberry.type(
description=(
"The Relay compliant `PageInfo` type, containing data necessary "
"to paginate this connection."
)
)
class PageInfo:
has_next_page: bool = strawberry.field(
description="When paginating forwards, are there more items?"
)
has_previous_page: bool = strawberry.field(
description="When paginating backwards, are there more items?"
)
start_cursor: Optional[str] = strawberry.field(
description="When paginating backwards, the cursor to continue."
)
end_cursor: Optional[str] = strawberry.field(
description="When paginating forwards, the cursor to continue."
)
NodeType = TypeVar("NodeType")
@strawberry.type
class Edge(Generic[NodeType]):
cursor: str = strawberry.field(description="A cursor for use in pagination")
node: NodeType = strawberry.field(description="The item at the end of the edge")
def get_list_offsets(
list_length: int,
before: Optional[str] = None,
after: Optional[str] = None,
first: Optional[int] = None,
last: Optional[int] = None,
) -> Tuple[int, int]:
slice_start = 0
slice_end = list_length
before_offset = get_offset_with_default(before, list_length)
after_offset = get_offset_with_default(after, -1)
start_offset = max(slice_start - 1, after_offset, -1) + 1
end_offset = min(slice_end, before_offset, list_length)
if isinstance(first, int):
if first < 0:
raise ValueError("Argument 'first' must be a non-negative integer.")
end_offset = min(end_offset, start_offset + first)
if isinstance(last, int):
if last < 0:
raise ValueError("Argument 'last' must be a non-negative integer.")
start_offset = max(start_offset, end_offset - last)
return (start_offset, end_offset) Usage: import strawberry
from graphql_relay import offset_to_cursor
from .relay import PageInfo, Edge, get_list_offsets
@strawberry.type
class User:
name: str
@strawberry.type
class FriendsConnection:
page_info: PageInfo = strawberry.field(
description="Pagination data for this connection"
)
edges: List[Edge[User]] = strawberry.field(
description="Contains the nodes in this connection"
)
@classmethod
def from_list(
cls,
list,
before: Optional[str] = None,
after: Optional[str] = None,
first: Optional[int] = None,
last: Optional[int] = None,
) -> "FriendsConnection":
list_length = len(list)
if list_length == 0:
return FriendsConnection(
edges=[],
page_info=PageInfo(
has_next_page=False,
has_previous_page=False,
start_cursor=None,
end_cursor=None,
),
)
start_offset, end_offset = get_list_offsets(
list_length, before=before, after=after, first=first, last=last,
)
edges = [
FriendsEdge(
cursor=offset_to_cursor(start_offset + index),
node=value,
)
for index, value in enumerate(queryset[start_offset:end_offset])
]
page_info = PageInfo(
start_cursor=edges[0].cursor if edges else None,
end_cursor=edges[-1].cursor if edges else None,
has_previous_page=False,
has_next_page=isinstance(first, int) and list_length > len(edges),
)
return FriendsConnection(edges=edges, page_info=page_info)
@strawberry.type
class Query:
@strawberry.field
def friends(
self,
info,
before: Optional[str] = None,
after: Optional[str] = None,
first: Optional[int] = None,
last: Optional[int] = None,
) -> FriendsConnection:
list_of_friends = [User(name=f"Friend {value}") for value in range(20)]
return FriendsConnection.from_list(list_of_friends, before=before, after=after, first=first, last=last) There is definite scope to build some of these helpers into Strawberry but IMO it's probably best to leave it in userland until we can settle on some good patterns. Edit: Updated code to use Generics |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
I like how use used make_dataclass! This might be a good approach for dynamic type generation 😊 Have you seen generic types? For relay connection and edges they might be a good solution: https://github.com/strawberry-graphql/strawberry/blob/master/tests/schema/test_generics.py#L6-L35 |
Beta Was this translation helpful? Give feedback.
I like how use used make_dataclass! This might be a good approach for dynamic type generation 😊
Have you seen generic types? For relay connection and edges they might be a good solution: https://github.com/strawberry-graphql/strawberry/blob/master/tests/schema/test_generics.py#L6-L35