Best way to construct a GraphQL query string in Python
Question:
I’m trying to do this (see title), but it’s a bit complicated since the string I’m trying to build has to have the following properties:
- mulitiline
- contains curly braces
- I want to inject variables into it
Using a normal ''''''
multiline string makes injecting variables difficult. Using multiple f-strings makes injecting variables easy, but every curly brace, of which there are a lot, has to be doubled. And an f
has to be prepended to each line. On the other hand, if I try using format
, it also gets confused by all the curly braces.
Is there a better way that I haven’t considered yet?
Answers:
You can use the “”” multiline string method. For injecting variables, make sure to use the $ sign while defining the string and use the variables object in the JSON parameter of the requests.post method.
Here is an example. ContactInput
is one of the types I defined in my GraphQL schema.
query = """
mutation ($input:[ContactInput!]!) {
AddContacts(contacts: $input) {
user_id
}
}
"""
variables = {'input': my_arrofcontacts}
r = requests.post(url, json={'query': query , 'variables': variables})
There is no need to construct graphQL strings to inject variables.
This is even abusing graphQL as it was designed to separate query definition and variables (arguments). It’s safer, easier to validate etc.
Just learn how to use query variables
to pass arguments. This is described in docs and many tutorials. You should know and use in practice this technique. Trying to use string injections can at least prove lack of your knowledge.
If you aren’t writting a graphql editor or other tool then you shouldn’t use string operations at all. BTW even editor shouldn’t operate on strings but on AST.
It’s rarely required to operate on strings in graphql, f.e. to let user choose required answer (response part of query) elements/fragments.
Update
Operating using objects is more elastic/usable – e.g. easily solves conditional problems: How to conditionally include an argument in a GraphQL query?
you can use the following package graphql-query
For example, for the query
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
we have the following code
from graphql_query import Argument, Operation, Query, Fragment, Field
comparisonFields = Fragment(
name="comparisonFields",
type="Character",
fields=["name", "appearsIn", Field(name="friends", fields=["name"])]
)
leftComparison = Query(
name="hero",
alias="leftComparison",
arguments=[Argument(name="episode", value="EMPIRE")],
fields=[comparisonFields]
)
rightComparison = Query(
name="hero",
alias="rightComparison",
arguments=[Argument(name="episode", value="JEDI")],
fields=[comparisonFields]
)
operation = Operation(
type="query",
queries=[leftComparison, rightComparison],
fragments=[comparisonFields]
)
print(operation.render())
# query {
# leftComparison: hero(
# episode: EMPIRE
# ) {
# ...comparisonFields
# }
#
# rightComparison: hero(
# episode: JEDI
# ) {
# ...comparisonFields
# }
# }
#
# fragment comparisonFields on Character {
# name
# appearsIn
# friends {
# name
# }
# }
Another tool you could use is py-graphql-mapper.
Given a schema it will generate python code corresponding to GraphQL types, so if you would like to send a request containing a query, simply, the only thing you would need is to instantiate the corresponding python object, assign the arguments you need and send an HTTP post using the ‘export_gql_source’ property of the python class you used for the query.
More details here:
I’m trying to do this (see title), but it’s a bit complicated since the string I’m trying to build has to have the following properties:
- mulitiline
- contains curly braces
- I want to inject variables into it
Using a normal ''''''
multiline string makes injecting variables difficult. Using multiple f-strings makes injecting variables easy, but every curly brace, of which there are a lot, has to be doubled. And an f
has to be prepended to each line. On the other hand, if I try using format
, it also gets confused by all the curly braces.
Is there a better way that I haven’t considered yet?
You can use the “”” multiline string method. For injecting variables, make sure to use the $ sign while defining the string and use the variables object in the JSON parameter of the requests.post method.
Here is an example. ContactInput
is one of the types I defined in my GraphQL schema.
query = """
mutation ($input:[ContactInput!]!) {
AddContacts(contacts: $input) {
user_id
}
}
"""
variables = {'input': my_arrofcontacts}
r = requests.post(url, json={'query': query , 'variables': variables})
There is no need to construct graphQL strings to inject variables.
This is even abusing graphQL as it was designed to separate query definition and variables (arguments). It’s safer, easier to validate etc.
Just learn how to use query variables
to pass arguments. This is described in docs and many tutorials. You should know and use in practice this technique. Trying to use string injections can at least prove lack of your knowledge.
If you aren’t writting a graphql editor or other tool then you shouldn’t use string operations at all. BTW even editor shouldn’t operate on strings but on AST.
It’s rarely required to operate on strings in graphql, f.e. to let user choose required answer (response part of query) elements/fragments.
Update
Operating using objects is more elastic/usable – e.g. easily solves conditional problems: How to conditionally include an argument in a GraphQL query?
you can use the following package graphql-query
For example, for the query
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
we have the following code
from graphql_query import Argument, Operation, Query, Fragment, Field
comparisonFields = Fragment(
name="comparisonFields",
type="Character",
fields=["name", "appearsIn", Field(name="friends", fields=["name"])]
)
leftComparison = Query(
name="hero",
alias="leftComparison",
arguments=[Argument(name="episode", value="EMPIRE")],
fields=[comparisonFields]
)
rightComparison = Query(
name="hero",
alias="rightComparison",
arguments=[Argument(name="episode", value="JEDI")],
fields=[comparisonFields]
)
operation = Operation(
type="query",
queries=[leftComparison, rightComparison],
fragments=[comparisonFields]
)
print(operation.render())
# query {
# leftComparison: hero(
# episode: EMPIRE
# ) {
# ...comparisonFields
# }
#
# rightComparison: hero(
# episode: JEDI
# ) {
# ...comparisonFields
# }
# }
#
# fragment comparisonFields on Character {
# name
# appearsIn
# friends {
# name
# }
# }
Another tool you could use is py-graphql-mapper.
Given a schema it will generate python code corresponding to GraphQL types, so if you would like to send a request containing a query, simply, the only thing you would need is to instantiate the corresponding python object, assign the arguments you need and send an HTTP post using the ‘export_gql_source’ property of the python class you used for the query.
More details here: