- Notifications
You must be signed in to change notification settings - Fork39
A library to help construct a graphql-py server supporting react-relay
License
graphql-python/graphql-relay-py
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
GraphQL-relay-py is theRelay library forGraphQL-core.
It allows the easy creation of Relay-compliant servers using GraphQL-core.
GraphQL-Relay-Py is a Python port ofgraphql-relay-js,while GraphQL-Core is a Python port ofGraphQL.js,the reference implementation of GraphQL for JavaScript.
Since version 3, GraphQL-Relay-Py and GraphQL-Core support Python 3.6 and above only.For older versions of Python, you can use version 2 of these libraries.
A basic understanding of GraphQL and of the GraphQL Python implementation is neededto provide context for this library.
An overview of GraphQL in general is available in theREADME for theSpecification for GraphQL.
This library is designed to work with thetheGraphQL-CorePython reference implementation of a GraphQL server.
An overview of the functionality that a Relay-compliant GraphQL server should provideis in theGraphQL Relay Specificationon theRelay website.That overview describes a simple set of examples that existastests in this repository.A good way to get started with this repository is to walk through that documentationand the corresponding tests in this library together.
Install Relay Library for GraphQL Python
pip install graphql-corepip install graphql-relay
When building a schema forGraphQL,the provided library functions can be used to simplify the creation of Relay patterns.
All the functions that are explained in the following sections must beimported from the top level of thegraphql_relay
package, like this:
fromgraphql_relayimportconnection_definitions
Helper functions are provided for both building the GraphQL typesfor connections and for implementing theresolve
method for fieldsreturning those types.
connection_args
returns the arguments that fields should provide whenthey return a connection type that supports bidirectional pagination.forward_connection_args
returns the arguments that fields should provide whenthey return a connection type that only supports forward pagination.backward_connection_args
returns the arguments that fields should provide whenthey return a connection type that only supports backward pagination.connection_definitions
returns aconnection_type
and its associatededgeType
, given a name and a node type.connection_from_array
is a helper method that takes an array and thearguments fromconnection_args
, does pagination and filtering, and returnsan object in the shape expected by aconnection_type
'sresolve
function.cursor_for_object_in_connection
is a helper method that takes an array and amember object, and returns a cursor for use in the mutation payload.offset_to_cursor
takes the index of a member object in an arrayand returns an opaque cursor for use in the mutation payload.cursor_to_offset
takes an opaque cursor (created withoffset_to_cursor
)and returns the corresponding array index.
An example usage of these methods from thetest schema:
ship_edge,ship_connection=connection_definitions(ship_type,"Ship")faction_type=GraphQLObjectType(name="Faction",description="A faction in the Star Wars saga",fields=lambda: {"id":global_id_field("Faction"),"name":GraphQLField(GraphQLString,description="The name of the faction."),"ships":GraphQLField(ship_connection,description="The ships used by the faction.",args=connection_args,resolve=lambdafaction,_info,**args:connection_from_array( [get_ship(ship)forshipinfaction.ships],args ), ), },interfaces=[node_interface],)
This shows adding aships
field to theFaction
object that is a connection.It usesconnection_definitions(ship_type, "Ship")
to create the connectiontype, addsconnection_args
as arguments on this function, and then implementsthe resolver function by passing the array of ships and the arguments toconnection_from_array
.
Helper functions are provided for both building the GraphQL typesfor nodes and for implementing global IDs around local IDs.
node_definitions
returns theNode
interface that objects can implement,and returns thenode
root field to include on the query type.To implement this, it takes a function to resolve an ID to an object,and to determine the type of a given object.to_global_id
takes a type name and an ID specific to that type name,and returns a "global ID" that is unique among all types.from_global_id
takes the "global ID" created byto_global_id
, andreturns the type name and ID used to create it.global_id_field
creates the configuration for anid
field on a node.plural_identifying_root_field
creates a field that accepts a list ofnon-ID identifiers (like a username) and maps then to their correspondingobjects.
An example usage of these methods from thetest schema:
defget_node(global_id,_info):type_,id_=from_global_id(global_id)iftype_=="Faction":returnget_faction(id_)iftype_=="Ship":returnget_ship(id_)returnNone# pragma: no coverdefget_node_type(obj,_info,_type):ifisinstance(obj,Faction):returnfaction_type.namereturnship_type.namenode_interface,node_field=node_definitions(get_node,get_node_type)[:2]faction_type=GraphQLObjectType(name="Faction",description="A faction in the Star Wars saga",fields=lambda: {"id":global_id_field("Faction"),"name":GraphQLField(GraphQLString,description="The name of the faction."),"ships":GraphQLField(ship_connection,description="The ships used by the faction.",args=connection_args,resolve=lambdafaction,_info,**args:connection_from_array( [get_ship(ship)forshipinfaction.ships],args ), ), },interfaces=[node_interface],)query_type=GraphQLObjectType(name="Query",fields=lambda: {"rebels":GraphQLField(faction_type,resolve=lambda_obj,_info:get_rebels()),"empire":GraphQLField(faction_type,resolve=lambda_obj,_info:get_empire()),"node":node_field, },)
This usesnode_definitions
to construct theNode
interface and thenode
field; it usesfrom_global_id
to resolve the IDs passed in the implementationof the function mapping ID to object. It then uses theglobal_id_field
method tocreate theid
field onFaction
, which also ensures implements thenode_interface
. Finally, it adds thenode
field to the query type, using thenode_field
returned bynode_definitions
.
A helper function is provided for building mutations withsingle inputs and client mutation IDs.
mutation_with_client_mutation_id
takes a name, input fields, output fields,and a mutation method to map from the input fields to the output fields,performing the mutation along the way. It then creates and returns a fieldconfiguration that can be used as a top-level field on the mutation type.
An example usage of these methods from thetest schema:
classIntroduceShipMutation:def__init__(self,shipId,factionId,clientMutationId=None):self.shipId=shipIdself.factionId=factionIdself.clientMutationId=clientMutationIddefmutate_and_get_payload(_info,shipName,factionId,**_input):new_ship=create_ship(shipName,factionId)returnIntroduceShipMutation(shipId=new_ship.id,factionId=factionId)ship_mutation=mutation_with_client_mutation_id("IntroduceShip",input_fields={"shipName":GraphQLInputField(GraphQLNonNull(GraphQLString)),"factionId":GraphQLInputField(GraphQLNonNull(GraphQLID)), },output_fields={"ship":GraphQLField(ship_type,resolve=lambdapayload,_info:get_ship(payload.shipId) ),"faction":GraphQLField(faction_type,resolve=lambdapayload,_info:get_faction(payload.factionId) ), },mutate_and_get_payload=mutate_and_get_payload,)mutation_type=GraphQLObjectType("Mutation",fields=lambda: {"introduceShip":ship_mutation})
This code creates a mutation namedIntroduceShip
, which takes a factionID and a ship name as input. It outputs theFaction
and theShip
inquestion.mutate_and_get_payload
then gets each input field as keywordparameter, performs the mutation by constructing the new ship, then returnsan object that will be resolved by the output fields.
Our mutation type then creates theintroduceShip
field using the returnvalue ofmutation_with_client_mutation_id
.
After cloning this repository from GitHub,we recommend usingPoetryto create a test environment. With poetry installed,you do this with the following command:
poetry install
You can then run the complete test suite like this:
poetry run pytest
In order to run only a part of the tests with increased verbosity,you can add pytest options, like this:
poetry run pytest tests/node -vv
In order to check the code style with flake8, use this:
poetry run flake8
Use thetox
command to run the test suite with differentPython versions and perform all additional source code checks.You can also restrict tox to an individual environment, like this:
poetry run tox -e py39
About
A library to help construct a graphql-py server supporting react-relay
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors8
Uh oh!
There was an error while loading.Please reload this page.