Skip to content

Commit 3a237da

Browse files
author
Naor Matania
committed
Add gql compiler
1 parent 459d5eb commit 3a237da

23 files changed

+55879
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ The main features of GQL are:
4141
* Supports [File uploads](https://gql.readthedocs.io/en/latest/usage/file_upload.html)
4242
* [gql-cli script](https://gql.readthedocs.io/en/latest/gql-cli/intro.html) to execute GraphQL queries from the command line
4343
* [DSL module](https://gql.readthedocs.io/en/latest/advanced/dsl_module.html) to compose GraphQL queries dynamically
44+
* [Compiler](https://gql.readthedocs.io/en/latest/advanced/compiler.html) to compile query files to strongly typed classes
4445

4546
## Installation
4647

docs/advanced/compiler.rst

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
Compiling queries to strongly typed classes
2+
===========================================
3+
4+
It is possible to create strongly typed classes for querying graphql based on graphql schema.
5+
It is similar to how other libraries generate code for API schema types like gRPC and Thrift.
6+
7+
The compliation is very helpful for building GraphQL based SDK in a fast and safe way as it helps you guarantee the following:
8+
9+
1. GraphQL queries are valid - Queries are now validated in compile time and not just in runtime
10+
2. Python usage of query classes is valid - Using static typing (like mypy - http://mypy-lang.org/)
11+
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
12+
13+
Usage
14+
-----
15+
16+
After installation you should compile your queries with running
17+
18+
.. code-block:: bash
19+
20+
gql-compiler {schema_library} {graphql_library}
21+
22+
* ``schema_library`` is where the folder where the graphql schema (or schemas) are located
23+
* ``graphql_library`` is where you locate then queries that you'd like to compile (``Query``, ``Mutation`` and ``Subscription`` are supported)
24+
25+
Example:
26+
27+
In the ``graphql_library`` create the file ``query.graphql``:
28+
29+
.. code-block::
30+
31+
query DogQuery($id: String!) {
32+
dog(id: $id) {
33+
id
34+
name
35+
breed
36+
age
37+
}
38+
}
39+
40+
After compilation it will create the file ``query.py`` in the same
41+
folder:
42+
43+
.. code-block:: python
44+
45+
#!/usr/bin/env python3
46+
# @generated AUTOGENERATED file. Do not Change!
47+
48+
from dataclasses import dataclass, field
49+
from gql_client.runtime.variables import encode_variables
50+
from gql import gql, Client
51+
from gql.transport.exceptions import TransportQueryError
52+
from functools import partial
53+
from numbers import Number
54+
from typing import Any, AsyncGenerator, Dict, List, Generator, Optional
55+
from time import perf_counter
56+
from dataclasses_json import DataClassJsonMixin, config
57+
58+
from gql_client.runtime.enum_utils import enum_field_metadata
59+
from .enum.dog_breed import DogBreed
60+
61+
62+
# fmt: off
63+
QUERY: List[str] = ["""
64+
query DogQuery($id: String!) {
65+
dog(id: $id) {
66+
id
67+
name
68+
breed
69+
age
70+
}
71+
}
72+
"""
73+
]
74+
75+
76+
class DogQuery:
77+
@dataclass(frozen=True)
78+
class DogQueryData(DataClassJsonMixin):
79+
@dataclass(frozen=True)
80+
class Dog(DataClassJsonMixin):
81+
id: str
82+
name: Optional[str]
83+
breed: Optional[DogBreed] = field(metadata=enum_field_metadata(DogBreed))
84+
age: Optional[int]
85+
86+
dog: Optional[Dog]
87+
88+
# fmt: off
89+
@classmethod
90+
def execute(cls, client: Client, id: str) -> Optional[DogQueryData.Dog]:
91+
variables: Dict[str, Any] = {"id": id}
92+
new_variables = encode_variables(variables)
93+
response_text = client.execute(
94+
gql("".join(set(QUERY))), variable_values=new_variables
95+
)
96+
res = cls.DogQueryData.from_dict(response_text)
97+
return res.dog
98+
99+
# fmt: off
100+
@classmethod
101+
async def execute_async(cls, client: Client, id: str) -> Optional[DogQueryData.Dog]:
102+
variables: Dict[str, Any] = {"id": id}
103+
new_variables = encode_variables(variables)
104+
response_text = await client.execute_async(
105+
gql("".join(set(QUERY))), variable_values=new_variables
106+
)
107+
res = cls.DogQueryData.from_dict(response_text)
108+
return res.dog
109+
110+
111+
An example for using the generated class:
112+
113+
.. code-block:: python
114+
115+
from gql import Client
116+
from gql.transport.aiohttp import AIOHTTPTransport
117+
from gql_client.runtime.graphql_client import GraphqlClient
118+
from query import DogQuery
119+
120+
transport = AIOHTTPTransport(url="http://.../graph/query")
121+
client = Client(transport=transport, fetch_schema_from_transport=True)
122+
result = DogQuery.execute(client, id="1000")
123+
124+
125+
Custom Scalars
126+
--------------
127+
128+
If your graphql schema contains custom scalars you should create python
129+
configuration file with the definitions of the custom scalars and use in
130+
the compilation command with ``--config_path`` option. In the
131+
configuration file you should have ``custom_scalars`` variable of type
132+
``Dict[str, CustomScalar]``. Simple example:
133+
134+
.. code-block:: python
135+
136+
from gql_client.compiler.renderer_dataclasses import CustomScalar
137+
from typing import Dict
138+
139+
custom_scalars: Dict[str, CustomScalar] = {
140+
"Cursor": CustomScalar(
141+
name="Cursor",
142+
type=str,
143+
),
144+
}
145+
146+
147+
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.
148+
149+
More features
150+
-------------
151+
152+
- Create fragments query files and share them between other query files
153+
- Compiler has an option to only verify compiled query files without
154+
re-genrating them (``--verify`` option)
155+
- Compiler can be configured to raise an error if queries use
156+
deprecated fields (``--allow-deprecated`` option)

docs/advanced/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ Advanced
88
logging
99
local_schema
1010
dsl_module
11+
compiler

gql/compiler/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""The :mod:`compiler` package includes compiler for compiling graphql query files to strongly typed classes.
2+
More details can be found in [Documentation](https://gql.readthedocs.io/en/latest/advanced/compiler.html)
3+
"""

0 commit comments

Comments
 (0)