Skip to content

Add gql compiler #185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The main features of GQL are:
* Supports [File uploads](https://gql.readthedocs.io/en/latest/usage/file_upload.html)
* [gql-cli script](https://gql.readthedocs.io/en/latest/gql-cli/intro.html) to execute GraphQL queries from the command line
* [DSL module](https://gql.readthedocs.io/en/latest/advanced/dsl_module.html) to compose GraphQL queries dynamically
* [Compiler](https://gql.readthedocs.io/en/latest/advanced/compiler.html) to compile query files to strongly typed classes

## Installation

Expand Down
155 changes: 155 additions & 0 deletions docs/advanced/compiler.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
Compiling queries to strongly typed classes
===========================================

It is possible to create strongly typed classes for querying graphql based on graphql schema.
It is similar to how other libraries generate code for API schema types like gRPC and Thrift.

The compilation is very helpful for building GraphQL based SDK in a fast and safe way as it helps you guarantee the following:

1. GraphQL queries are valid - Queries are now validated in compile time and not just in runtime
2. Python usage of query classes is valid - Using static typing (like mypy - http://mypy-lang.org/)
3. Changes over time in graphql schema doesn't break existing SDKs - You can use verify flag of the compiler and integrate it in your CI/CD

Usage
-----

After installation you should compile your queries with running

.. code-block:: bash

gql-compiler {schema_library} {graphql_library}

* ``schema_library`` is where the folder where the graphql schema (or schemas) are located
* ``graphql_library`` is where you locate then queries that you'd like to compile (``Query``, ``Mutation`` and ``Subscription`` are supported)

Example:

In the ``graphql_library`` create the file ``query.graphql``:

.. code-block::

query DogQuery($id: String!) {
dog(id: $id) {
id
name
breed
age
}
}

After compilation it will create the file ``query.py`` in the same
folder:

.. code-block:: python

#!/usr/bin/env python3
# @generated AUTOGENERATED file. Do not Change!

from dataclasses import dataclass, field
from gql.compiler.runtime.variables import encode_variables
from gql import gql, Client
from gql.transport.exceptions import TransportQueryError
from functools import partial
from numbers import Number
from typing import Any, AsyncGenerator, Dict, List, Generator, Optional
from dataclasses_json import DataClassJsonMixin, config

from gql.compiler.runtime.enum_utils import enum_field_metadata
from .enum.dog_breed import DogBreed


# fmt: off
QUERY: List[str] = ["""
query DogQuery($id: String!) {
dog(id: $id) {
id
name
breed
age
}
}
"""
]


class DogQuery:
@dataclass(frozen=True)
class DogQueryData(DataClassJsonMixin):
@dataclass(frozen=True)
class Dog(DataClassJsonMixin):
id: str
name: Optional[str]
breed: Optional[DogBreed] = field(metadata=enum_field_metadata(DogBreed))
age: Optional[int]

dog: Optional[Dog]

# fmt: off
@classmethod
def execute(cls, client: Client, id: str) -> Optional[DogQueryData.Dog]:
variables: Dict[str, Any] = {"id": id}
new_variables = encode_variables(variables)
response_text = client.execute(
gql("".join(set(QUERY))), variable_values=new_variables
)
res = cls.DogQueryData.from_dict(response_text)
return res.dog

# fmt: off
@classmethod
async def execute_async(cls, client: Client, id: str) -> Optional[DogQueryData.Dog]:
variables: Dict[str, Any] = {"id": id}
new_variables = encode_variables(variables)
response_text = await client.execute_async(
gql("".join(set(QUERY))), variable_values=new_variables
)
res = cls.DogQueryData.from_dict(response_text)
return res.dog


An example for using the generated class:

.. code-block:: python

from gql import Client
from gql.transport.aiohttp import AIOHTTPTransport
from gql_client.runtime.graphql_client import GraphqlClient
from query import DogQuery

transport = AIOHTTPTransport(url="http://.../graph/query")
client = Client(transport=transport, fetch_schema_from_transport=True)
result = DogQuery.execute(client, id="1000")


Custom Scalars
--------------

If your graphql schema contains custom scalars you should create python
configuration file with the definitions of the custom scalars and use in
the compilation command with ``--config_path`` option. In the
configuration file you should have ``custom_scalars`` variable of type
``Dict[str, CustomScalar]``. Simple example:

.. code-block:: python

from gql_client.compiler.renderer_dataclasses import CustomScalar
from typing import Dict

custom_scalars: Dict[str, CustomScalar] = {
"Cursor": CustomScalar(
name="Cursor",
type=str,
),
}


CustomScalar also has ``encoder``, ``decoder`` and ``mm_field`` fields that can be used when the custom scalar is defined with complex type that requires encoding-decoding from the string that is being sent in the graphql response.

More features
-------------

- Create fragments query files and share them between other query files
- Compiler has an option to only verify compiled query files without
re-generating them (``--verify`` option)
- Compiler can be configured to raise an error if queries use
deprecated fields (``--allow-deprecated`` option)
1 change: 1 addition & 0 deletions docs/advanced/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Advanced
logging
local_schema
dsl_module
compiler
5 changes: 5 additions & 0 deletions gql/compiler/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""The :mod:`compiler` package includes compiler for compiling graphql query files to
strongly typed classes.
More details can be found in
[Documentation](https://gql.readthedocs.io/en/latest/advanced/compiler.html)
"""
Loading