From the docs at http://docs.graphene-python.org/en/latest/relay/connection/
A connection is a vitaminized version of a List
I'm a beginner to GraphQL, so maybe "vitaminized" is a GraphQL term that I don't understand yet, but I have no idea what this actually means, and neither did any of my colleagues. We'd appreciate a clarification to the documentation around this specifically.
More generally, I don't really understand the difference between _Type_s and _Node_s, and when I would use one or the other. I understand that _Node_s have something to do with Relay, but I'm not quite sure what. Some explanation about what the difference is, how to choose between them, and how _Node_s and _Connection_s fit into the picture would be great.
A node is a type. If you want to use relay's connection specification for pagination then your Object Type will need to implement the node interface.
Basically a Connection is a way to paginate a list of data. Each connection is a list of edges that have a cursor and a node. The node is your actual data object.
lets take the example provided
class Ship(graphene.ObjectType):
ship_type = String()
def resolve_ship_type(instance, info):
return instance.ship_type
class Meta:
interfaces = (Node,)
class ShipConnection(Connection):
total_count = Int() # i've found count on connections very useful!
def resolve_total_count(instance, info):
return get_count_of_all_ships()
class Meta:
node = Ship
class Edge:
other = String()
def resolve_other(instance, info):
return "This is other: " + instance.node.other
I've included a ShipNode implementation and some resolvers to make the example of how this all works together
so this gives us the ability to create query for lists of ships.
lets say we have a root level query
class Query(graphene.ObjectType):
ships = relay.ConnectionField(ShipConnection)
def resolve_ships(self, info):
return get_ships_from_database_or_something_idk_its_your_implmentation()
schema = graphene.Schema(query=Query)
now in graphiql we can query for ships,
the following query will give us the first 10 ships
query{
ships(first: 10){
totalCount,
edges{
cursor,
node{
id,
shipType
}
}
}
}
you can also do last
query{
ships(last: 10){
or you can take one of your cursors and do the first 10 after that
query{
ships(first: 10, after: $someCursor){
this is really useful because now you have pagination for basically free, especially if you're using something like graphene-django. Its amazing
You can either add to your first count to get the infinite scrolling lists like on fb page, or you can paginate by pages using cursors.
there is also a pageInfo object on connections that give you info that is helpful for paginating.
query {
ships(first: 10){
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
edges{ ... ect
}
}
}
I hope this helps!
Also something to note. Relay has their own special id system. Each id needs to be unique for every node in your whole application. In graphene-django they combine id with node type to achieve this.
There are two helper functions to help with this id'ing
from graphql_relay import from_global_id, to_global_id
if you want more information here are the relay docs for the connection specification
https://facebook.github.io/relay/docs/graphql-connections.html
the graphene docs should probably link to them so that people can get a more indepth understanding of this specification
Also one last note. You do not have to use relay to use connections. It is only a specification. I use Apollo as my frontend client and find connections very handy!
@danpalmer
@BossGrand That's a great explanation, thanks! I agree with @danpalmer that the Graphene documentation is missing all of this detail.
That said, could you comment some more on what the cursors actually are when working with Graphene? How are you handling them? Are they actually database cursors? If so, where do you store them and when do you release them?
Hi, I just started working on GraphQL stuff again, was reading through docs, and realised I still don't know what "vitaminized" means, could someone explain this and/or update the docs?
@BossGrand How to calculate pageInfo in resolver method as you mentioned above, such is:
class Query(graphene.ObjectType):
ships = relay.ConnectionField(ShipConnection)
def resolve_ships(self, info):
return get_ships_from_database_or_something_idk_its_your_implmentation()
schema = graphene.Schema(query=Query)
Is there any method for calculate pageInfo in "graphene.Connection" ? I'm using
graphene-sqlalchemy.
@syrusakbary @adamcharnock
@hheimbuerger the cursor is not a database cursor. Its kind of similar but its relays own thing. Its just a way to keep track of where you are in pagination
@keyeMyria i can't tell you how to calculate pageinfo. I think the graphene.Connection handles that. But not sure
Following up on this, I still don't know what "vitaminized" means.
A connection is an enhanced structure for items that can be paginated, enabling the following capabilities for the client:
I'm not sure if this statement makes more clear what connections are for, but I would love to improve the docs with something that can be properly understood for both newbies and experienced GraphQL devs.
Thoughts? How do you think we can make connections clear for everyone?
This description sounds great! Much better than “vitaminized”, which I’m not sure is even a word.
On 31 Aug 2018, at 11:07, Syrus Akbary notifications@github.com wrote:
A connection is an enhanced structure for items that can be paginated, enabling the following capabilities for the client:
The ability to paginate through the list.
The ability to ask for information about the connection itself, like totalCount or pageInfo.
The ability to ask for information about the edge itself, like cursor or friendshipTime.
The ability to change how our backend does pagination since the user just uses opaque cursors.
I'm not sure if this statement makes more clear what connections are for, but I would love to improve the docs with something that can be properly understood for both newbies and experienced GraphQL devs.
Thoughts? How do you think we can make connections clear for everyone?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
Hi @BossGrand awesome example there. But i want to ask something, lets say i have this field in Query schema
ship = ConnectionField(ShipConnection)
so i need to resolve this by using resolve_ship(self, info): return get_all_ships()
But in the query schema i only want to query count. By doing this approach isn't that we waste the get_all_ships function? Can you give me better approach to this problem, to only get specific count without querying all the ships?
Thank you
@fanadol you can specify your own Connection
For example is this is the one my company uses
class HelloOfficeConnection(graphene.Connection):
count = graphene.Int()
def resolve_count(self, info):
return self.length
class Meta:
abstract = True
and then on the query you can do this
query {
ships(first: 10){
count
}
}
and count will the be the total of get_all_ships
Thank you for your answer @BossGrand , but i still cannot figure out what is self.length here. How do i get the list of get_all_ships inside connection class ? iam using native graphene for this
nvm, i got it by doing len(self.edges) in resolve_count.
Thank you very much for the help!
How does graphene pick out the id of a ship? It's not listed as a field.
@Lucretiel its node, in the doc they said:
A Node is an Interface provided by graphene.relay that contains a single field id (which is a ID!). Any object that inherits from it has to implement a get_node method for retrieving a Node by an id.
further read: https://docs.graphene-python.org/en/latest/relay/nodes/
Informative thread but there are still a couple of things unclear.
def resolve_ships(self, info):
return get_ships_from_database_or_something_idk_its_your_implmentation()
First there is **kwargs missing in resolve_ships signature otherwise you can't use before, after and so on I think. If we add **kwargs and do not use them anywhere after argument still works. So there must be some magic going on in the background. Also, this example implies that we return all of the ships from our database and this is not really great for performance. If we specify after, then we should tell the database to search from there on and not return all of the results. Even if we would base64 decode after and used data in there to skip results while performing database search, the background magic would skip again on returned results and loose us the data.
So my question is basically how do we change the background magic. How do we control what data is encoded in the cursors and how can we access that data in a nice way so we can use it when we perform the database search instead of returning all of the items and then skipping over them?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Most helpful comment
A node is a type. If you want to use relay's connection specification for pagination then your Object Type will need to implement the node interface.
Basically a Connection is a way to paginate a list of data. Each connection is a list of edges that have a cursor and a node. The node is your actual data object.
lets take the example provided
I've included a ShipNode implementation and some resolvers to make the example of how this all works together
so this gives us the ability to create query for lists of ships.
lets say we have a root level query
now in graphiql we can query for ships,
the following query will give us the first 10 ships
you can also do last
or you can take one of your cursors and do the first 10 after that
this is really useful because now you have pagination for basically free, especially if you're using something like graphene-django. Its amazing
You can either add to your first count to get the infinite scrolling lists like on fb page, or you can paginate by pages using cursors.
there is also a pageInfo object on connections that give you info that is helpful for paginating.
I hope this helps!
Also something to note. Relay has their own special id system. Each id needs to be unique for every node in your whole application. In graphene-django they combine id with node type to achieve this.
There are two helper functions to help with this id'ing
if you want more information here are the relay docs for the connection specification
https://facebook.github.io/relay/docs/graphql-connections.html
the graphene docs should probably link to them so that people can get a more indepth understanding of this specification
Also one last note. You do not have to use relay to use connections. It is only a specification. I use Apollo as my frontend client and find connections very handy!
@danpalmer