graphql-python / graphene-django

Build powerful, efficient, and flexible GraphQL APIs with seamless Django integration.
http://docs.graphene-python.org/projects/django/en/latest/
MIT License
4.31k stars 769 forks source link

Implement filtering without using relay #1199

Open daniel-skale opened 3 years ago

daniel-skale commented 3 years ago

Is your feature request related to a problem? Please describe. Trying to implement filtering to graphene django, but i don't use Relay (Apollo). And would like the feature to be implemented without having to lock into using relay.

Describe alternatives you've considered Currently have to follow this example: https://www.howtographql.com/graphql-python/7-filtering/

Additional context Would love to know if anyone who is using graphene at scale and how they are filtering queries if they aren't using relay in the stack. Thanks!

wodCZ commented 3 years ago

We are using Graphene in several production apps, thousands of registered users level scale.

We are using Apollo client on most of the frontend stacks, on few apps we use SWR to fetch our Graphene backends.

We are using the suggested way from documentation for filtering - we are making use of the GraphQL Cursor Connection Specification, which comes from the Relay team, but is not exclusive to the Relay GraphQL client.

Graphene just provides Relay-Compatible structure, which is basically nothing else than "hey, pagination using GraphQL is best done this way" and similar. In other words, you can use relay-compatible structure without being tied to a Relay Graphql client. And you probably should design your backend relay-compatible, since it's considered a best-practice.

Personally I like Saleor's attitude (Saleor is a open source headless ecommerce system built on top of graphene-django). From their documentation:

Saleor uses cursor-based pagination which means that instead of using page numbers, each item is assigned a unique cursor and API clients can ask for a certain number of items following or preceding a given item. There are two types of lists in GraphQL: [Foo] is a simple list. It is used to query a list containing several items. A good example of a simple list could be a query for product variants which returns a list with a manageable number of results. FooConnection represents a more complex list. When queried, it will return an unknown or large number of results. Pagination is used to help you handle large amounts of items returned by the connection list type.

They have a slightly modified Connection class, but the idea remains the same - use Connections for complex lists where you need pagination and filtering, and for simple lists with finite amount of items you can use simple lists.

daniel-skale commented 3 years ago

@wodCZ thank you so much for the reply, will definitely need to read up on the relay-compatible concepts. Since we are not locked down to use relay but you mentioned that graphene still provides the Relay-compatible structure such as nodes, connections, etc. that means that I can use relay.Node as has been shown in the docs here without using relay?

wodCZ commented 3 years ago

@daniel-skale yes, that's right - there's no harm in using relay.Node.

Since we don't use the Relay client, we've skipped few features. For example the Node Root Field is something we haven't found a use for, so we just didn't register the node root field.

Thing to keep in mind is that when you use relay.Node (or DjangoObjectType) the ID your GraphQL outputs is replaced with Base64 string that includes the Node name and the original ID (because from Relay's specification, each Node must have a unique ID). It's a bit uglier, but it's actually something Apollo client is doing too with it's cache read more. No need to worry about that much, just keep that in mind, took me few failures to realise the ID coming in mutation from client is not the numerical id, but the Node ID that must be extracted using from_global_id (https://docs.graphene-python.org/projects/django/en/latest/mutations/#relay).