Best way to work with relay
See original GitHub issueHello, I started working with graphene recently and I decided to create a small API to learn the framework.
I have an external service that expose some functions, let’s call them get_properties()
and get_property_rooms(property_id)
. I started creating two DataLoader
, one to fetch the properties of the user and another to fetch the rooms of a specific property.
class PropertiesLoader(DataLoader):
def get_cache_key(self, key):
return key['context'].user
def batch_load_fn(self, keys):
return Promise.resolve([get_properties() for key in keys])
class RoomsLoader(DataLoader):
def get_cache_key(self, key):
return key['context'].user + key['property_id']
def batch_load_fn(self, keys):
return Promise.resolve([get_property_rooms(key['property_id']) for key in keys])
properties_loader = PropertiesLoader()
rooms_loader = RoomsLoader()
Then I created two nodes and two connections, one for the properties and another for the rooms.
class Room(graphene.ObjectType):
class Meta:
interfaces = (relay.Node, )
name = graphene.String(description='The room name')
class RoomConnection(relay.Connection):
class Meta:
node = Room
class Property(graphene.ObjectType):
class Meta:
interfaces = (relay.Node, )
name = graphene.String(description='The name of the property')
rooms = relay.ConnectionField(RoomConnection, description='The rooms of the property')
@staticmethod
def resolve_rooms(root, info, **kwargs):
return rooms_loader.load(root.id)
@classmethod
def get_node(cls, info, id):
return properties_loader.load(
{'context': info.context}
).then(lambda properties: [
item for item in properties if item.id == int(id)
][0])
class PropertyConnection(relay.Connection):
class Meta:
node = Property
class RelayQuery(graphene.ObjectType):
property = relay.Node.Field(Property)
properties = relay.ConnectionField(PropertyConnection)
room = graphene.Field(Room,
id=graphene.ID(required=True),
property_id=graphene.ID(required=True))
rooms = relay.ConnectionField(RoomConnection,
property_id=graphene.ID(required=True))
@staticmethod
def resolve_properties(root, info, **kwargs):
return properties_loader.load({'context': info.context})
@staticmethod
def resolve_room(root, info, id, property_id, **kwargs):
_, _id = relay.Node.from_global_id(id)
_, _property_id = relay.Node.from_global_id(property_id)
return rooms_loader.load(
{'context': info.context, 'id': int(_property_id)}
).then(lambda rooms: [
item for item in rooms if item.id == int(_id)
][0])
@staticmethod
def resolve_rooms(root, info, property_id, **kwargs):
_, id = relay.Node.from_global_id(property_id)
return rooms_loader.load({'context': info.context, 'property_id': int(id)})
schema = graphene.Schema(query=RelayQuery)
As you may notice from the RelayQuery
, I have a room field which accepts two graphene.ID()
. One for the room id and another for the property id.
I want to know if there is a better way to do this? It would be really nice if I could use a relay.Node.Field
for the room field in the RelayQuery
, pass the property id as argument and implement the get_node
method in the Room
node like the following example:
class Room(graphene.ObjectType):
class Meta:
interfaces = (relay.Node, )
name = graphene.String(description='The room name')
@classmethod
def get_node(cls, info, id, property_id):
return rooms_loader.load(
{'context': info.context, 'property_id': int(property_id)}
).then(lambda rooms: [
item for item in rooms if item.id == int(id)
][0])
class RelayQuery(graphene.ObjectType):
...
room = relay.Node.Field(Room, property_id=graphene.ID(required=True))
...
Please let me know if there is a better approach to do this and if yes, could you update the documentation in the relay section? I want to be able to do the following queries:
query {
properties {
edges {
node {
id, name,
rooms {
edges {
node {
id, name
}
}
}
}
}
}
}
query {
rooms(propertyId: "UHJvcGVydHk6Mjg5Mg==") {
edges {
node {
id, name
}
}
}
}
query {
property(id: "UHJvcGVydHk6Mjg5Mg==") {
id, name,
rooms {
edges {
node {
id, name
}
}
}
}
}
query {
room(id: "Um9vbTo2", propertyId: "UHJvcGVydHk6Mjg5Mg==") {
id, name
}
}
Issue Analytics
- State:
- Created 5 years ago
- Comments:8 (5 by maintainers)
Top GitHub Comments
Sure it’s totally up to you. What I would say is that the best way to architect your schema is to think about what makes the most sense for how the front end client wants to use the data. You can always improve efficiency in the backend but if your client has to work around types or fields that don’t make sense to how the data is actually being used then you might as well be using a traditional REST api.
Anyway I’m going to close this issue as it doesn’t seem like there is anything that needs to change in Graphene.
Thanks for the idea @jkimbo but I see a problem with your approach. Since my method
get_room
accepts the room id and the property id, there is no need to fetch the property. All I need is to pass the property id and room id to my service to get the room details. Going through the property is an unnecessary job because it will pass in theget_node
method from theProperty
node. Since I’m usingDataLoader
the data is in cache but even then I don’t want my code to go through unnecessary methods to fetch the data. I would prefer to use a room graphene field at root and translate the global id to proper id using thefrom_global_id
method