Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pagination support #175

Closed
un33k opened this issue Oct 8, 2019 · 18 comments
Closed

Add pagination support #175

un33k opened this issue Oct 8, 2019 · 18 comments
Assignees
Labels
enhancement New feature or request help wanted Extra attention is needed
Milestone

Comments

@un33k
Copy link

un33k commented Oct 8, 2019

[ x ] Feature Request
[ x ] Documentation

If pagination is supported, be nice to include it in documentations. If there is no plan to support it, it'd be nice to provide an example for developers to role their own. Otherwise, let's keep this issue around to track the pagination feature.

@jaydenwindle
Copy link
Member

jaydenwindle commented Oct 14, 2019

Right now there is no built-in support for pagination (as far as I know). It would definitely be neat to support this though!

One way to implement basic pagination would be to use something like this:

@strawberry.type
class Query:

    @strawberry.field
    def numbers(self, info, first: int = None, skip: int = None) -> typing.List[int]:
        numbers = range(1000)
        return numbers[skip:first]

This approach could be abstracted into a decorator for use in multiple queries as well.

If you'd like to add support for pagination using this approach, or write some documentation for it, a PR would be welcome :)

@jaydenwindle jaydenwindle changed the title Pagination support Add pagination support Oct 14, 2019
@jaydenwindle jaydenwindle added enhancement New feature or request help wanted Extra attention is needed Hacktoberfest labels Oct 14, 2019
@patrick91
Copy link
Member

@jaydenwindle thanks for answering 😊 at PyCon DE someone asked for Relay support, maybe we could write some helpers for that, I kinda this something here:

https://github.com/strawberry-graphql/strawberry-swapi/blob/master/utils.py

I think Graphene has something for arrays already, so we could import that. I don't really have much experience with Relay, so I'd happy to see someone working on this :)

@denizdogan
Copy link

Are there any simple examples of how to implement a decorator that's meant to be used in combination with @strawberry.field? I gave it a half-assed shot, but quickly got lost in type annotation jungle. I only recently started trying out this library, but it seems that type annotations are very central to the Strawberry library?

Any help would be appreciated, even just a simple example :) Sorry if this is the wrong place to ask.

@patrick91
Copy link
Member

@denizdogan what did you try already? I'd like to know mainly because I'd like to make the library as user friendly as possible :)

I'll try to come up with an example tomorrow :)

@denizdogan
Copy link

@patrick91 As I said, I didn't really try very hard :) But I slapped together a simple example which shows where I get stuck:

def strawberry_limit(limit=100):
    def inner(func):
        def replacement(*args, **kwargs):
            output = inner(*args, **kwargs)
            return output[:limit]
        return replacement
    return inner

@strawberry.type
class UserType:
    uuid: str

@strawberry.type
class Query:
    @strawberry.field
    @strawberry_limit(limit=10)  # does order matter?
    def users(self, info) -> List[UserType]:
        return User.objects.all()

The above style gives me the following error:

strawberry.exceptions.MissingReturnAnnotationError: Return annotation missing for field "replacement", did you forget to add it?

So this is when I realize that I have to take care to not lose the type annotations, so I start off by using @functools.wraps for my decorator definition.

I tried again using the following:

def strawberry_limit(func):
    @wraps(func)
    def replacement(*args, limit: int = None, **kwargs):
        output = func(*args, **kwargs)
        if limit is not None:
            return output[:limit]
        return output

    return replacement


@strawberry.type
class Query:
    @strawberry_limit  # again, does order matter?
    @strawberry.field
    def users(self, info) -> List[UserType]:
        return User.objects.all()

The above doesn't raise any uncaught exception, but I do get:

{
  "data": null,
  "errors": [
    {
      "message": "Type Query must define one or more fields.",
      "locations": null,
      "path": null
    }
  ]
}

Am I close? :)

@patrick91
Copy link
Member

oh, when we use @strawberry.field we add an attribute on the function, so that we can find it later (to created the field), but maybe this approach is not the best one as this is breaking in this case.

This is where we do it:

https://github.com/strawberry-graphql/strawberry/blob/master/strawberry/field.py#L175-L176

one solution (for now) would be do put that attribute yourself, but it is not ideal. I'll think of something better.

What happens if you switch the order in the second case? I'd try myself, but I'm on the phone right now :)

@denizdogan
Copy link

denizdogan commented Nov 24, 2019

If I switch the order of the decorators, I get:

"message": "Unknown argument 'limit' on field 'users' of type 'Query'.",

Whenever you get the time and strength to give this a go, I'm very interested, but it's not really a pressing issue for me right now, so don't sweat it :)

I've been considering that maybe using a decorator for this kind of thing is not really the ideal way to implement something like pagination. I could see us wrapping the return value in some lazy wrapper, as such:

def users(self, info) -> Paginated[List[User]]:
    return Paginated(info, Users.objects.all(), limit=100)

Then the logic would happen in the Paginated class instead of the decorator. That way, we could avoid messing with the magic that strawberry.field implements and stay out of its way? Obviously, I haven't tried this way to know if it's more accessible than the decorator idea, but I thought I'd mention it. :)

@patrick91
Copy link
Member

Quick update on this, we recently released support for generic types with the goal of making it easier to create pagination types. They'd work like this:

import strawberry


@strawberry.type
class PageInfo:
    hasNext: bool
    lastCursor: str


T = TypeVar("T")


@strawberry.type
class Paginated(Generic[T]):
    count: Optional[int] = None
    items: T

    @classmethod
    def paginate(cls, data: Sequence[T], page: int):
        ...

and you'd use them like this:

@strawberry.type
class Query:
     @strawberry.field
     def users(self, page: int) -> Paginated[User]:
          return Paginated.paginate(User.all(), page)

This would create the PaginatedUser type for you.

We plan to add relay pagination and a simple page pagination as built-in :)

@patrick91 patrick91 added this to the Version 1 milestone Aug 10, 2020
@jkimbo jkimbo self-assigned this Aug 8, 2021
@memark
Copy link
Member

memark commented Oct 1, 2021

Any progress on this?

@akhilputhiry
Copy link

waiting for this to implement in my project

@patrick91
Copy link
Member

@memark not yet, are you interested in a Relay pagination or basic pagination?

@memark
Copy link
Member

memark commented Oct 5, 2021

@patrick91 Basic pagination would be just fine!

@patrick91
Copy link
Member

@memark cool! maybe I can write a guide, it shouldn't be too difficult to roll your own since we have support for generics. Would that help?

I wouldn't commit to an API design for basic pagination just yet 😊

@memark
Copy link
Member

memark commented Oct 5, 2021

@patrick91 Yes, that would be very helpful!

@aryaniyaps aryaniyaps mentioned this issue Oct 13, 2021
15 tasks
@rossm6
Copy link

rossm6 commented Nov 25, 2021

I'm looking to move to strawberry from graphene but don't want to before pagination is supported. How far off is it? I see basic looks to be done. What about cursor based?

@aryaniyaps
Copy link
Contributor

aryaniyaps commented Jan 3, 2022

I'm looking to move to strawberry from graphene but don't want to before pagination is supported. How far off is it? I see basic looks to be done. What about cursor based?

Cursor based pagination can already be implemented in strawberry, the docs just need to be updated (#1345)

@vasilistotskas
Copy link

could someone give me a cursor based pagination example ? thanks

@patrick91
Copy link
Member

I'm going to close this as we now have docs for pagination (see #1345)

We can always include helpers in future (or in an external library), but I'd discuss those in a new issue :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

10 participants