Skip to content

Commit 00b61d5

Browse files
authored
New transport AIOHTTPWebsocketsTransport (#478)
1 parent ede1350 commit 00b61d5

15 files changed

+4619
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import asyncio
2+
import logging
3+
4+
from gql import Client, gql
5+
from gql.transport.aiohttp_websockets import AIOHTTPWebsocketsTransport
6+
7+
logging.basicConfig(level=logging.INFO)
8+
9+
10+
async def main():
11+
12+
transport = AIOHTTPWebsocketsTransport(
13+
url="wss://countries.trevorblades.com/graphql"
14+
)
15+
16+
# Using `async with` on the client will start a connection on the transport
17+
# and provide a `session` variable to execute queries on this connection
18+
async with Client(
19+
transport=transport,
20+
) as session:
21+
22+
# Execute single query
23+
query = gql(
24+
"""
25+
query getContinents {
26+
continents {
27+
code
28+
name
29+
}
30+
}
31+
"""
32+
)
33+
result = await session.execute(query)
34+
print(result)
35+
36+
# Request subscription
37+
subscription = gql(
38+
"""
39+
subscription {
40+
somethingChanged {
41+
id
42+
}
43+
}
44+
"""
45+
)
46+
async for result in session.subscribe(subscription):
47+
print(result)
48+
49+
50+
asyncio.run(main())

docs/intro.rst

+21-19
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,27 @@ which needs the :code:`aiohttp` dependency, then you can install GQL with::
3636

3737
The corresponding between extra dependencies required and the GQL classes is:
3838

39-
+---------------------+----------------------------------------------------------------+
40-
| Extra dependencies | Classes |
41-
+=====================+================================================================+
42-
| aiohttp | :ref:`AIOHTTPTransport <aiohttp_transport>` |
43-
+---------------------+----------------------------------------------------------------+
44-
| websockets | :ref:`WebsocketsTransport <websockets_transport>` |
45-
| | |
46-
| | :ref:`PhoenixChannelWebsocketsTransport <phoenix_transport>` |
47-
| | |
48-
| | :ref:`AppSyncWebsocketsTransport <appsync_transport>` |
49-
+---------------------+----------------------------------------------------------------+
50-
| requests | :ref:`RequestsHTTPTransport <requests_transport>` |
51-
+---------------------+----------------------------------------------------------------+
52-
| httpx | :ref:`HTTPTXTransport <httpx_transport>` |
53-
| | |
54-
| | :ref:`HTTPXAsyncTransport <httpx_async_transport>` |
55-
+---------------------+----------------------------------------------------------------+
56-
| botocore | :ref:`AppSyncIAMAuthentication <appsync_iam_auth>` |
57-
+---------------------+----------------------------------------------------------------+
39+
+---------------------+------------------------------------------------------------------+
40+
| Extra dependencies | Classes |
41+
+=====================+==================================================================+
42+
| aiohttp | :ref:`AIOHTTPTransport <aiohttp_transport>` |
43+
| | |
44+
| | :ref:`AIOHTTPWebsocketsTransport <aiohttp_websockets_transport>` |
45+
+---------------------+------------------------------------------------------------------+
46+
| websockets | :ref:`WebsocketsTransport <websockets_transport>` |
47+
| | |
48+
| | :ref:`PhoenixChannelWebsocketsTransport <phoenix_transport>` |
49+
| | |
50+
| | :ref:`AppSyncWebsocketsTransport <appsync_transport>` |
51+
+---------------------+------------------------------------------------------------------+
52+
| requests | :ref:`RequestsHTTPTransport <requests_transport>` |
53+
+---------------------+------------------------------------------------------------------+
54+
| httpx | :ref:`HTTPTXTransport <httpx_transport>` |
55+
| | |
56+
| | :ref:`HTTPXAsyncTransport <httpx_async_transport>` |
57+
+---------------------+------------------------------------------------------------------+
58+
| botocore | :ref:`AppSyncIAMAuthentication <appsync_iam_auth>` |
59+
+---------------------+------------------------------------------------------------------+
5860

5961
.. note::
6062

docs/modules/gql.rst

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Sub-Packages
2121
client
2222
transport
2323
transport_aiohttp
24+
transport_aiohttp_websockets
2425
transport_appsync_auth
2526
transport_appsync_websockets
2627
transport_exceptions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
gql.transport.aiohttp_websockets
2+
================================
3+
4+
.. currentmodule:: gql.transport.aiohttp_websockets
5+
6+
.. automodule:: gql.transport.aiohttp_websockets
7+
:member-order: bysource

docs/transports/aiohttp.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ Reference: :class:`gql.transport.aiohttp.AIOHTTPTransport`
1010
.. note::
1111

1212
GraphQL subscriptions are not supported on the HTTP transport.
13-
For subscriptions you should use the :ref:`websockets transport <websockets_transport>`.
13+
For subscriptions you should use a websockets transport:
14+
:ref:`WebsocketsTransport <websockets_transport>` or
15+
:ref:`AIOHTTPWebsocketsTransport <aiohttp_websockets_transport>`.
1416

1517
.. literalinclude:: ../code_examples/aiohttp_async.py
1618

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.. _aiohttp_websockets_transport:
2+
3+
AIOHTTPWebsocketsTransport
4+
==========================
5+
6+
The AIOHTTPWebsocketsTransport is an alternative to the :ref:`websockets_transport`,
7+
using the `aiohttp` dependency instead of the `websockets` dependency.
8+
9+
It also supports both:
10+
11+
- the `Apollo websockets transport protocol`_.
12+
- the `GraphQL-ws websockets transport protocol`_
13+
14+
It will propose both subprotocols to the backend and detect the supported protocol
15+
from the response http headers returned by the backend.
16+
17+
.. note::
18+
For some backends (graphql-ws before `version 5.6.1`_ without backwards compatibility), it may be necessary to specify
19+
only one subprotocol to the backend. It can be done by using
20+
:code:`subprotocols=[AIOHTTPWebsocketsTransport.GRAPHQLWS_SUBPROTOCOL]`
21+
or :code:`subprotocols=[AIOHTTPWebsocketsTransport.APOLLO_SUBPROTOCOL]` in the transport arguments.
22+
23+
This transport allows to do multiple queries, mutations and subscriptions on the same websocket connection.
24+
25+
Reference: :class:`gql.transport.aiohttp_websockets.AIOHTTPWebsocketsTransport`
26+
27+
.. literalinclude:: ../code_examples/aiohttp_websockets_async.py
28+
29+
.. _version 5.6.1: https://github.com/enisdenjo/graphql-ws/releases/tag/v5.6.1
30+
.. _Apollo websockets transport protocol: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md
31+
.. _GraphQL-ws websockets transport protocol: https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md

docs/transports/async_transports.rst

+1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ Async transports are transports which are using an underlying async library. The
1212
aiohttp
1313
httpx_async
1414
websockets
15+
aiohttp_websockets
1516
phoenix
1617
appsync

gql/cli.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def get_parser(with_examples: bool = False) -> ArgumentParser:
159159
"aiohttp",
160160
"phoenix",
161161
"websockets",
162+
"aiohttp_websockets",
162163
"appsync_http",
163164
"appsync_websockets",
164165
],
@@ -286,7 +287,12 @@ def autodetect_transport(url: URL) -> str:
286287
"""Detects which transport should be used depending on url."""
287288

288289
if url.scheme in ["ws", "wss"]:
289-
transport_name = "websockets"
290+
try:
291+
import websockets # noqa: F401
292+
293+
transport_name = "websockets"
294+
except ImportError: # pragma: no cover
295+
transport_name = "aiohttp_websockets"
290296

291297
else:
292298
assert url.scheme in ["http", "https"]
@@ -338,6 +344,11 @@ def get_transport(args: Namespace) -> Optional[AsyncTransport]:
338344

339345
return WebsocketsTransport(url=args.server, **transport_args)
340346

347+
elif transport_name == "aiohttp_websockets":
348+
from gql.transport.aiohttp_websockets import AIOHTTPWebsocketsTransport
349+
350+
return AIOHTTPWebsocketsTransport(url=args.server, **transport_args)
351+
341352
else:
342353

343354
from gql.transport.appsync_auth import AppSyncAuthentication

0 commit comments

Comments
 (0)