Comments (13)
We don't support generic types like Page<T>
, as representing them client-side can be really tricky. Is it a template type? Is it a generic interface? Lots of questions and complexity we chose to avoid, at least for now.
@devknoll, you're pretty much on the money with that answer. We have a convention that we use at Facebook that we call Connection
. We have some helper functions on the server side that keeps us from duplicating the work of implementing this convention, and avoids mistakes. That gets you most of the value you would get from having some kind of template type or generics.
At Facebook, it looks like this:
type UserConnection {
count: Int
nodes: [User]
edges: [UserConnectionEdge]
}
type UserConnectionEdge {
node: User
cursor: String
}
You can imagine replacing User
with any type here and the type model still works. The nodes
field is a shorthand for edges { node { ... } }
when all you want are the nodes at the end of the connection. edges
also has cursor
as a utility for performing pagination.
Pagination in GraphQL at Facebook looks something like:
type User {
...
friends(after: String, first: Int, before: String, last: Int): UserConnection
...
}
So here, the friends
field on a User
accepts a couple arguments. after
accepts a value that cursor
gave you in a prior query, and first
limits how many items are returned. before
and last
are the same ideas, but for paginating from the back of a list towards the front.
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 offset
, limit
. There are some issues with this approach (for example, if the front of the list changes often, or when deep into a very long list), but it's simplicity is compelling.
In this model, you might instead type things as:
type User {
...
friends(offset: Int, limit: Int): [User]
...
}
Now there's no additional Connection
type at all; I would suggest that offset
and limit
work here just as they do in SQL.
from graphql-spec.
@RavenHursT, ensure that your variable is not required.
For example, this query should error if no value is provided for $after
since it's required to be a non-null string (String!
).
query Example($after: String!) {
someConnection(after: $after) { count }
}
Where this example which accepts null (just String
), should not error if no value is provided for $after
. This is considered best practice for using variables with pagination.
query Example($after: String) {
someConnection(after: $after) { count }
}
from graphql-spec.
@leebyron What do you think about documenting that pattern somewhere more visible than this issue?
from graphql-spec.
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:
type FriendList {
count: Int,
edges: [User!]
}
type User {
friends(first: Int, after: ID): FriendList
}
This should hopefully answer both of your questions. Pagination would be implemented by using the first
and after
field arguments on friends
, and the count
accessed would be on the type FriendList
, a wrapper over the list of User
s.
I'm not entirely sure if this has been changed, so I would love to see @leebyron pitch in too 😄
from graphql-spec.
@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 FriendList
type for every type you want to paginate over PostList
etc.
This relates to the question I mentioned, whether or not is possible to create generic types just like the native List
.
from graphql-spec.
💃
@leebyron thanks! I'll play around with this ideas. Thank you!
from graphql-spec.
@graue Yep, we're definitely planning on doing so!
from graphql-spec.
@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:
type UserConnectionEdge { node: User cursor: String }
- why do you guys put the cursor of every node along with that node? What's the use case behind this decision? Wouldn't it be enough to have a before and after (first and last item) cursor at the connection level?
- why do you call it connection? I'm just trying to understand the abstraction properly. I've been thinking about it in terms of pages. I'm wondering if there are any caveats.
- I'm thinking to implement pagination like this. I'd love feedback.
type UserPage {
count: Int
nodes: [User]
after: String
before: String
}
and the entry point would be the same
type User {
...
friends(offset: Int, limit: Int): [User]
...
}
Thank you so much in advance.
from graphql-spec.
@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:
after
accepts a value thatcursor
gave you in a prior query
Ok.. and I've definitely gotten that to work by taking a look at the edge cursors on the /graphql
response in my browser. Problem is.. how do I make this work for the first "page" of items, when I have no "prior query" to work from?
I tried leaving after
as undefined
in my query, but I only got the following error:
Uncaught Invariant Violation: callsFromGraphQL(): Expected a declared value for variable, $curs.
It seems, if you define first
and after
in your container's fragment, then they're required parameters. But I have no value for after
, so how in the world does one initialize this?
Thanks!
from graphql-spec.
@leebyron yeah.. turns out that you have to explicitly set default variables w/ null
, not just allow them to be undefined
:
Thanks.
from graphql-spec.
That sounds like a bug actually. What GraphQL library are you using? graph-js?
from graphql-spec.
We're using express-graphql
from graphql-spec.
We don't support generic types like
Page<T>
well it's working: https://launchpad.graphql.com/nxqjv8k4l7
With this Page
template type for example:
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),
},
},
});
from graphql-spec.
Related Issues (20)
- (Graphql-schema) Cannot query field "closeDiscussion" on type "Mutation" HOT 1
- GraphQL spec is contradictory: can selection sets be empty or not? HOT 1
- Allow to return Input Object types HOT 1
- Formalize Global Object Identification.
- Grammar should have single root node HOT 2
- [Feature request]: Support non-list variables for list arguments HOT 9
- Redundant field aliases identical to field names
- Document Level Directives HOT 7
- Coercing Variable Values when hasValue is not true and defaultValue does NOT exist HOT 7
- Strawman: mention of non-self-describing responses
- why not allow directive on field argument ? HOT 2
- On specifying ordered vs unordered enum definitions HOT 5
- Casting Error in GraphQL C# Library HOT 2
- Is there a reason graphql floats do not support Infinity and NaN? HOT 3
- Composite types is not clearly defined HOT 1
- Unclear spec for array with fragments HOT 1
- What must be the result of executing only one fragment on a list of union or interface type? HOT 3
- Interface conditions fragments may not be used across spreads, discouraging reusability HOT 2
- Hey HOT 1
- Really
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphql-spec.