-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Pagination? #4
Comments
As far as what I've seen in the past on GraphQL, if you wanted pagination, one schema you could use to achieve that would be:
This should hopefully answer both of your questions. Pagination would be implemented by using the I'm not entirely sure if this has been changed, so I would love to see @leebyron pitch in too 😄 |
@devknoll thanks for your response. Yup, I guess something among those lines is always possible. There are some caveats with an approach is that you'd have to duplicate the This relates to the question I mentioned, whether or not is possible to create generic types just like the native |
We don't support generic types like @devknoll, you're pretty much on the money with that answer. We have a convention that we use at Facebook that we call At Facebook, it looks like this:
You can imagine replacing Pagination in GraphQL at Facebook looks something like:
So here, the So again, this is just a convention that Facebook uses. GraphQL itself doesn't know what pagination is, it just enables patterns like these. Underlying code is responsible for actually reading these arguments and applying them in a sensible way. You could also imagine a much simpler pagination model that just does In this model, you might instead type things as:
Now there's no additional |
💃 @leebyron thanks! I'll play around with this ideas. Thank you! |
@leebyron What do you think about documenting that pattern somewhere more visible than this issue? |
@graue Yep, we're definitely planning on doing so! |
@leebyron I've been playing with this and it works great. The duplication in the server is not a problem as you mentioned, a function to define a connection type for a given types solves the problem just fine. I have a couple of follow up questions and I was hoping you could shed some light:
and the entry point would be the same
Thank you so much in advance. |
@leebyron Thanks for your informational reply. It's helped a lot! So sorry to revive this old thread.. but I'm completely lost as to how to implement one piece of what you said:
Ok.. and I've definitely gotten that to work by taking a look at the edge cursors on the I tried leaving
It seems, if you define Thanks! |
@RavenHursT, ensure that your variable is not required. For example, this query should error if no value is provided for
Where this example which accepts null (just
|
@leebyron yeah.. turns out that you have to explicitly set default variables w/ Thanks. |
That sounds like a bug actually. What GraphQL library are you using? graph-js? |
We're using |
well it's working: https://launchpad.graphql.com/nxqjv8k4l7 With this function Page(Type) {
return new ObjectType({
name: `Page${Type}`,
description: `A simple pagination method
- nodes contains the actual list of data
- cursor is the end cursor or falsy if there are no more pages`,
fields: {
nodes: {type: new NonNull(new List(new NonNull(Type)))},
cursor: {type: String},
},
});
}
const QueryType = new ObjectType({
name: 'Query',
fields: {
foos: {
type: Page(Foo),
args: listArgs,
resolve: (_, args) =>getFoos(args),
},
},
}); |
Add work items section
* streamline stream execution Currently, these spec changes introduce a new internal function named `ResolveFieldGenerator` that is suggested parallels `ResolveFieldValue`. This function is used during field execution such that if the stream directive is specified, it is called instead of `ResolveFieldValue`. The reference implementation, however, does not require any such function, simply utilizing the result of `ResolveFieldValue`. With incremental delivery, collections completed by `CompleteValue` should be explicitly iterated using a well-defined iterator, such that the iterator can be passed to `ExecuteStreamField`. But this does not require a new internal function to be specified/exposed. Moreover, introducing this function causes a mixing of concerns between the `ExecuteField` and `CompleteValue` algorithms; Currently, if stream is specified for a field, `ExecuteField` extracts the iterator and passes it to `CompleteValue`, while if stream is not specified, the `ExecuteField` passes the collection, i.e. the iterable, not the iterator. In the stream case, this shunts some of the logic checking the validity of resolution results into field execution. In fact, it exposes a specification "bug" => in the stream case, no checking is actually done that `ResolveFieldGenerator` returns an iterator! This change removes `ResolveFieldGenerator` and with it some complexity, and brings it in line with the reference implementation. The reference implementation contains some simplification of the algorithm for the synchronous iterator case (we don't have to preserve the iterator on the StreamRecord, because there will be no early close required and we don't have to set isCompletedIterator, beacuse we don't have to create a dummy payload for termination of the asynchronous stream), We could consider also removing these bits as well, as they are an implementation detail in terms of how our dispatcher is managing its iterators, but that should be left for another change. * run prettier
* streamline stream execution Currently, these spec changes introduce a new internal function named `ResolveFieldGenerator` that is suggested parallels `ResolveFieldValue`. This function is used during field execution such that if the stream directive is specified, it is called instead of `ResolveFieldValue`. The reference implementation, however, does not require any such function, simply utilizing the result of `ResolveFieldValue`. With incremental delivery, collections completed by `CompleteValue` should be explicitly iterated using a well-defined iterator, such that the iterator can be passed to `ExecuteStreamField`. But this does not require a new internal function to be specified/exposed. Moreover, introducing this function causes a mixing of concerns between the `ExecuteField` and `CompleteValue` algorithms; Currently, if stream is specified for a field, `ExecuteField` extracts the iterator and passes it to `CompleteValue`, while if stream is not specified, the `ExecuteField` passes the collection, i.e. the iterable, not the iterator. In the stream case, this shunts some of the logic checking the validity of resolution results into field execution. In fact, it exposes a specification "bug" => in the stream case, no checking is actually done that `ResolveFieldGenerator` returns an iterator! This change removes `ResolveFieldGenerator` and with it some complexity, and brings it in line with the reference implementation. The reference implementation contains some simplification of the algorithm for the synchronous iterator case (we don't have to preserve the iterator on the StreamRecord, because there will be no early close required and we don't have to set isCompletedIterator, beacuse we don't have to create a dummy payload for termination of the asynchronous stream), We could consider also removing these bits as well, as they are an implementation detail in terms of how our dispatcher is managing its iterators, but that should be left for another change. * run prettier
* streamline stream execution Currently, these spec changes introduce a new internal function named `ResolveFieldGenerator` that is suggested parallels `ResolveFieldValue`. This function is used during field execution such that if the stream directive is specified, it is called instead of `ResolveFieldValue`. The reference implementation, however, does not require any such function, simply utilizing the result of `ResolveFieldValue`. With incremental delivery, collections completed by `CompleteValue` should be explicitly iterated using a well-defined iterator, such that the iterator can be passed to `ExecuteStreamField`. But this does not require a new internal function to be specified/exposed. Moreover, introducing this function causes a mixing of concerns between the `ExecuteField` and `CompleteValue` algorithms; Currently, if stream is specified for a field, `ExecuteField` extracts the iterator and passes it to `CompleteValue`, while if stream is not specified, the `ExecuteField` passes the collection, i.e. the iterable, not the iterator. In the stream case, this shunts some of the logic checking the validity of resolution results into field execution. In fact, it exposes a specification "bug" => in the stream case, no checking is actually done that `ResolveFieldGenerator` returns an iterator! This change removes `ResolveFieldGenerator` and with it some complexity, and brings it in line with the reference implementation. The reference implementation contains some simplification of the algorithm for the synchronous iterator case (we don't have to preserve the iterator on the StreamRecord, because there will be no early close required and we don't have to set isCompletedIterator, beacuse we don't have to create a dummy payload for termination of the asynchronous stream), We could consider also removing these bits as well, as they are an implementation detail in terms of how our dispatcher is managing its iterators, but that should be left for another change. * run prettier
* streamline stream execution Currently, these spec changes introduce a new internal function named `ResolveFieldGenerator` that is suggested parallels `ResolveFieldValue`. This function is used during field execution such that if the stream directive is specified, it is called instead of `ResolveFieldValue`. The reference implementation, however, does not require any such function, simply utilizing the result of `ResolveFieldValue`. With incremental delivery, collections completed by `CompleteValue` should be explicitly iterated using a well-defined iterator, such that the iterator can be passed to `ExecuteStreamField`. But this does not require a new internal function to be specified/exposed. Moreover, introducing this function causes a mixing of concerns between the `ExecuteField` and `CompleteValue` algorithms; Currently, if stream is specified for a field, `ExecuteField` extracts the iterator and passes it to `CompleteValue`, while if stream is not specified, the `ExecuteField` passes the collection, i.e. the iterable, not the iterator. In the stream case, this shunts some of the logic checking the validity of resolution results into field execution. In fact, it exposes a specification "bug" => in the stream case, no checking is actually done that `ResolveFieldGenerator` returns an iterator! This change removes `ResolveFieldGenerator` and with it some complexity, and brings it in line with the reference implementation. The reference implementation contains some simplification of the algorithm for the synchronous iterator case (we don't have to preserve the iterator on the StreamRecord, because there will be no early close required and we don't have to set isCompletedIterator, beacuse we don't have to create a dummy payload for termination of the asynchronous stream), We could consider also removing these bits as well, as they are an implementation detail in terms of how our dispatcher is managing its iterators, but that should be left for another change. * run prettier
* streamline stream execution Currently, these spec changes introduce a new internal function named `ResolveFieldGenerator` that is suggested parallels `ResolveFieldValue`. This function is used during field execution such that if the stream directive is specified, it is called instead of `ResolveFieldValue`. The reference implementation, however, does not require any such function, simply utilizing the result of `ResolveFieldValue`. With incremental delivery, collections completed by `CompleteValue` should be explicitly iterated using a well-defined iterator, such that the iterator can be passed to `ExecuteStreamField`. But this does not require a new internal function to be specified/exposed. Moreover, introducing this function causes a mixing of concerns between the `ExecuteField` and `CompleteValue` algorithms; Currently, if stream is specified for a field, `ExecuteField` extracts the iterator and passes it to `CompleteValue`, while if stream is not specified, the `ExecuteField` passes the collection, i.e. the iterable, not the iterator. In the stream case, this shunts some of the logic checking the validity of resolution results into field execution. In fact, it exposes a specification "bug" => in the stream case, no checking is actually done that `ResolveFieldGenerator` returns an iterator! This change removes `ResolveFieldGenerator` and with it some complexity, and brings it in line with the reference implementation. The reference implementation contains some simplification of the algorithm for the synchronous iterator case (we don't have to preserve the iterator on the StreamRecord, because there will be no early close required and we don't have to set isCompletedIterator, beacuse we don't have to create a dummy payload for termination of the asynchronous stream), We could consider also removing these bits as well, as they are an implementation detail in terms of how our dispatcher is managing its iterators, but that should be left for another change. * run prettier
Hey facebook,
First of all, thanks for open sourcing this, I'm so excited I forgot how to even.
With that out of the way, I have a question, how is pagination implemented in the facebook schema? There is no mention of pagination in the spec so I assume is implemented adhoc using the primitives provided by the spec. A hint on how to model this common use case would be really appreciated.
What I have in mind is some is some
Page
type. Which yields the following question: is it possible to define generic types the same way that List is defined? Asking because ideally suchPage
type would bePage<T>
Also, I'd also appreciate some advice on how to model querying for the count of a list property or a paginated property. In 2.10.1 Types of fragments there is the following example:
There is no schema attached for that example, but as far as I understand, those fragments are trying to get the total number of friends of a user and the total number of likes of a Page. It is hard to say without the schema, but if we assume that a User has a List of Users, the fist fragment is really querying for the property
count
in each individual User of said list, and not the propertycount
in the list itself (which is the intention, arguably). Any light in this topic would be very much appreciated.Thanks for open sourcing this and for your time.
The text was updated successfully, but these errors were encountered: