@@ -113,6 +113,245 @@ query = gql('''
113
113
client.execute(query)
114
114
```
115
115
116
+ With a python version > 3.6, it is possible to execute GraphQL subscriptions using the websockets transport:
117
+
118
+ ``` python
119
+ from gql import gql, Client
120
+ from gql.transport.websockets import WebsocketsTransport
121
+
122
+ sample_transport = WebsocketsTransport(url = ' wss://your_server/graphql' )
123
+
124
+ client = Client(
125
+ transport = sample_transport,
126
+ fetch_schema_from_transport = True ,
127
+ )
128
+
129
+ query = gql('''
130
+ subscription yourSubscription {
131
+ ...
132
+ }
133
+ ''' )
134
+
135
+ for result in client.subscribe(query):
136
+ print (f " result = { result!s } " )
137
+ ```
138
+
139
+ Note: the websockets transport can also execute queries or mutations
140
+
141
+ # Async usage with asyncio
142
+
143
+ When using the ` execute ` or ` subscribe ` function directly on the client, the execution is synchronous.
144
+ It means that we are blocked until we receive an answer from the server and
145
+ we cannot do anything else while waiting for this answer.
146
+
147
+ It is also possible to use this library asynchronously using [ asyncio] ( https://docs.python.org/3/library/asyncio.html ) .
148
+
149
+ Async Features:
150
+ * Execute GraphQL subscriptions (See [ using the websockets transport] ( #Websockets-async-transport ) )
151
+ * Execute GraphQL queries, mutations and subscriptions in parallel
152
+
153
+ To use the async features, you need to use an async transport:
154
+ * [ AIOHTTPTransport] ( #HTTP-async-transport ) for the HTTP(s) protocols
155
+ * [ WebsocketsTransport] ( #Websockets-async-transport ) for the ws(s) protocols
156
+
157
+ ## HTTP async transport
158
+
159
+ This transport uses the [ aiohttp library] ( https://docs.aiohttp.org )
160
+
161
+ GraphQL subscriptions are not supported on the HTTP transport.
162
+ For subscriptions you should use the websockets transport.
163
+
164
+ ``` python
165
+ from gql import gql, Client
166
+ from gql.transport.aiohttp import AIOHTTPTransport
167
+ import asyncio
168
+
169
+ async def main ():
170
+
171
+ sample_transport = AIOHTTPTransport(
172
+ url = ' https://countries.trevorblades.com/graphql' ,
173
+ headers = {' Authorization' : ' token' }
174
+ )
175
+
176
+ async with Client(
177
+ transport = sample_transport,
178
+ fetch_schema_from_transport = True ,
179
+ ) as session:
180
+
181
+ # Execute single query
182
+ query = gql('''
183
+ query getContinents {
184
+ continents {
185
+ code
186
+ name
187
+ }
188
+ }
189
+ ''' )
190
+
191
+ result = await session.execute(query)
192
+
193
+ print (result)
194
+
195
+ asyncio.run(main())
196
+ ```
197
+
198
+ ## Websockets async transport
199
+
200
+ The websockets transport uses the apollo protocol described here:
201
+
202
+ [ Apollo websockets transport protocol] ( https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md )
203
+
204
+ This transport allows to do multiple queries, mutations and subscriptions on the same websocket connection
205
+
206
+ ``` python
207
+ import logging
208
+ logging.basicConfig(level = logging.INFO )
209
+
210
+ from gql import gql, Client
211
+ from gql.transport.websockets import WebsocketsTransport
212
+ import asyncio
213
+
214
+ async def main ():
215
+
216
+ sample_transport = WebsocketsTransport(
217
+ url = ' wss://countries.trevorblades.com/graphql' ,
218
+ ssl = True ,
219
+ headers = {' Authorization' : ' token' }
220
+ )
221
+
222
+ async with Client(
223
+ transport = sample_transport,
224
+ fetch_schema_from_transport = True ,
225
+ ) as session:
226
+
227
+ # Execute single query
228
+ query = gql('''
229
+ query getContinents {
230
+ continents {
231
+ code
232
+ name
233
+ }
234
+ }
235
+ ''' )
236
+ result = await session.execute(query)
237
+ print (result)
238
+
239
+ # Request subscription
240
+ subscription = gql('''
241
+ subscription {
242
+ somethingChanged {
243
+ id
244
+ }
245
+ }
246
+ ''' )
247
+ async for result in session.subscribe(subscription):
248
+ print (result)
249
+
250
+ asyncio.run(main())
251
+ ```
252
+
253
+ ### Websockets SSL
254
+
255
+ If you need to connect to an ssl encrypted endpoint:
256
+
257
+ * use _ wss_ instead of _ ws_ in the url of the transport
258
+ * set the parameter ssl to True
259
+
260
+ ``` python
261
+ import ssl
262
+
263
+ sample_transport = WebsocketsTransport(
264
+ url = ' wss://SERVER_URL:SERVER_PORT/graphql' ,
265
+ headers = {' Authorization' : ' token' },
266
+ ssl = True
267
+ )
268
+ ```
269
+
270
+ If you have a self-signed ssl certificate, you need to provide an ssl_context with the server public certificate:
271
+
272
+ ``` python
273
+ import pathlib
274
+ import ssl
275
+
276
+ ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT )
277
+ localhost_pem = pathlib.Path(__file__ ).with_name(" YOUR_SERVER_PUBLIC_CERTIFICATE.pem" )
278
+ ssl_context.load_verify_locations(localhost_pem)
279
+
280
+ sample_transport = WebsocketsTransport(
281
+ url = ' wss://SERVER_URL:SERVER_PORT/graphql' ,
282
+ ssl = ssl_context
283
+ )
284
+ ```
285
+
286
+ If you have also need to have a client ssl certificate, add:
287
+
288
+ ``` python
289
+ ssl_context.load_cert_chain(certfile = ' YOUR_CLIENT_CERTIFICATE.pem' , keyfile = ' YOUR_CLIENT_CERTIFICATE_KEY.key' )
290
+ ```
291
+
292
+ ### Websockets authentication
293
+
294
+ There are two ways to send authentication tokens with websockets depending on the server configuration.
295
+
296
+ 1 . Using HTTP Headers
297
+
298
+ ``` python
299
+ sample_transport = WebsocketsTransport(
300
+ url = ' wss://SERVER_URL:SERVER_PORT/graphql' ,
301
+ headers = {' Authorization' : ' token' },
302
+ ssl = True
303
+ )
304
+ ```
305
+
306
+ 2 . With a payload in the connection_init websocket message
307
+
308
+ ``` python
309
+ sample_transport = WebsocketsTransport(
310
+ url = ' wss://SERVER_URL:SERVER_PORT/graphql' ,
311
+ init_payload = {' Authorization' : ' token' },
312
+ ssl = True
313
+ )
314
+ ```
315
+
316
+ ### Async advanced usage
317
+
318
+ It is possible to send multiple GraphQL queries (query, mutation or subscription) in parallel,
319
+ on the same websocket connection, using asyncio tasks
320
+
321
+ ``` python
322
+
323
+ async def execute_query1 ():
324
+ result = await session.execute(query1)
325
+ print (result)
326
+
327
+ async def execute_query2 ():
328
+ result = await session.execute(query2)
329
+ print (result)
330
+
331
+ async def execute_subscription1 ():
332
+ async for result in session.subscribe(subscription1):
333
+ print (result)
334
+
335
+ async def execute_subscription2 ():
336
+ async for result in session.subscribe(subscription2):
337
+ print (result)
338
+
339
+ task1 = asyncio.create_task(execute_query1())
340
+ task2 = asyncio.create_task(execute_query2())
341
+ task3 = asyncio.create_task(execute_subscription1())
342
+ task4 = asyncio.create_task(execute_subscription2())
343
+
344
+ await task1
345
+ await task2
346
+ await task3
347
+ await task4
348
+ ```
349
+
350
+ Subscriptions tasks can be stopped at any time by running
351
+
352
+ ``` python
353
+ task.cancel()
354
+ ```
116
355
117
356
## Contributing
118
357
See [ CONTRIBUTING.md] ( CONTRIBUTING.md )
0 commit comments