gqlmod: Importing GraphQL¶
gqlmod allows you to import GraphQL Query (.gql
) files as modules and call
the queries and mutations defined there. It will validate your queries at
import time, to surface any problems as soon as possible.
gqlmod also defines mechanisms for handling different services (called providers) and different contexts with those services.
Using gqlmod¶
Summary¶
Install the
gqlmod
PyPI package, as well as any providers you needCall
gqlmod.enable_gql_import()
as soon as possible (maybe in your__main__.py
or top-level__init__.py
)Import your query file and start calling your queries.
Example¶
#~starwars~
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
homePlanet
}
}
}
import gqlmod
gqlmod.enable_gql_import()
from queries import HeroForEpisode
resp = HeroForEpisode(ep='JEDI')
assert not resp.errors
print(resp.data)
Writing Query Files¶
Query files are simply text files full of named GraphQL queries and mutations.
One addition is the provider declaration:
#~starwars~
This tells the system what provider to connect to these queries, and therfore how to actually query the service, what schema to validate against, etc.
The name of the provider should be in the provider’s docs.
Query functions¶
The generated functions have a specific form.
Query functions only take keyword arguments, matching the variables defined in the query. Optional and arguments with defaults may naturally be omitted.
The function returns a graphql.ExecutionResult
. It has the following
attributes:
data
: The result dataerrors
: A list of errors that occurred, or an empty list if none occurred
Note that wether query functions are synchronous or asynchronous is up to the provider; see its documentation.
Using different provider contexts¶
All installed providers are available at startup, initialized with no arguments.
For most services, this will allow you to execute queries as an anonymous user.
However, most applications will want to authenticate to the service. You can use
gqlmod.with_provider()
to provide this data to the provider.
gqlmod.with_provider()
is a context manager, and may be nested. That
is, you can globally authenticate as your app, but also in specific parts
authenticate as a user.
The specific arguments will vary by provider, but usually have this basic form:
with gqlmod.with_provider('spam-service', token=config['TOKEN']):
resp = spam_queries.GetMenu(amount_of_spam=None)
Major Providers¶
Here is a list of some maintained providers:
starwars
: Builtin! A demo provider that works on static constant data.cirrus-ci
: From gqlmod-cirrusci, connects to Cirrus CIgithub
: From gqlmod-github, connects to the GitHub v4 API
You may be able to discover a provider at this places:
Writing a Provider¶
Writing a provider is fairly staightforward.
Define a provider class
Add an entry point declaration
Provider Classes¶
A provider class is only required to be callable with a specific signature.
import graphql
class MyProvider:
def __init__(self, token=None):
self.token = token
def __call__(self, query, variables):
# Do stuff here
return graphql.ExecutionResult(
errors=[],
data={'spam': 'eggs'}
)
The arguments it takes are:
query
: (string) The query to give to the servervariables
: (dict) The variables for that query
The provider should return a graphql.ExecutionResult
as shown above.
Entry point¶
In order to be discoverable by gqlmod, providers must define entrypoints.
Specifically, in the graphql_providers
group under the name you want .gql
files to use. This can take a few different forms, depending on your project. A few examples:
[options.entry_points]
graphql_providers =
starwars = gqlmod_starwars:StarWarsProvider
setup(
# ...
entry_points={
'graphql_providers': [
'starwars = gqlmod_starwars:StarWarsProvider'
]
},
# ...
)
# This is for poetry-based projects
[tool.poetry.plugins.graphql_providers]
"starwars" = "gqlmod_starwars:StarWarsProvider'"
Helpers¶
In order to help with common cases, gqlmod ships with several helpers
Note that many of them have additional requirements, which are encapsulated in extras.
urllib¶
Helpers for using urllib to build a provider. You probably want
UrllibJsonProvider
.
Requires the no extras.
-
class
gqlmod.helpers.urllib.
UrllibJsonProvider
[source]¶ A
UrllibProvider
that uses a JSON-based POST
-
class
gqlmod.helpers.urllib.
UrllibProvider
[source]¶ Help build an HTTP-based provider based on requests.
You should fill in
endpoint
and possibly overridemodify_request()
.-
endpoint
= None¶ The URL to send requests to
-
modify_request
(req)[source]¶ Apply policies about the request, primarily authentication.
Accepts a
urllib.request.Request
object.
-
aiohttp¶
Helpers for using aiohttp
to build a provider.
Requires the aiohttp
extra.
-
class
gqlmod.helpers.aiohttp.
AiohttpProvider
[source]¶ Help build an HTTP-based provider based on aiohttp.
You should fill in
endpoint
and possibly overridemodify_request_args()
.-
endpoint
= None¶ The URL to send requests to.
-
timeout
= None¶ Timeout policy to use, if any.
-
use_json
= False¶ Whether a JSON-based or form-like request should be used.
-
Extensions¶
In addition to the core querying interface, providers may influence the import process in a few different ways. These are all implemented as optional methods on the provider instance.
get_schema_str()
¶
Providers may override the standard schema discovery mechanism by implementing
get_schema_str()
. This is useful for providers that don’t have a primary
service or don’t allow anonymous access at all.
This method must be synchronous. An async variation is not supported.
Default behavior: Issue a GraphQL introspection query via the standard query path.
Parameters: None.
Returns: A str
of the schema, in standard GraphQL schema
language.
codegen_extra_kwargs()
¶
Providers may add keyword arguments (variables) to the query call inside the generated module. These will be passed through the query pipeline back to the provider.
Default behavior: No additional variables are inserted.
Parameters:
graphql_ast
(positional,graphql.language.OperationDefinitionNode
): The AST of the GraphQL query in questionschema
(positional,graphql.type.GraphQLSchema
): The schema of the service
Returns: A dict
of the names mapping to either simple values or
ast.AST
instances. (Note that the returned AST will be embedded into
a right-hand expression context.)