|
| 1 | +.. _http_multipart_transport: |
| 2 | + |
| 3 | +HTTPMultipartTransport |
| 4 | +====================== |
| 5 | + |
| 6 | +This transport implements GraphQL subscriptions over HTTP using the `multipart subscription protocol`_ |
| 7 | +as implemented by Apollo GraphOS Router and other compatible servers. |
| 8 | + |
| 9 | +This provides an HTTP-based alternative to WebSocket transports for receiving streaming |
| 10 | +subscription updates. It's particularly useful when: |
| 11 | + |
| 12 | +- WebSocket connections are not available or blocked by infrastructure |
| 13 | +- You want to use standard HTTP with existing load balancers and proxies |
| 14 | +- The backend implements the multipart subscription protocol |
| 15 | + |
| 16 | +Reference: :class:`gql.transport.http_multipart_transport.HTTPMultipartTransport` |
| 17 | + |
| 18 | +.. note:: |
| 19 | + |
| 20 | + This transport is specifically designed for GraphQL subscriptions. While it can handle |
| 21 | + queries and mutations via the ``execute()`` method, standard HTTP transports like |
| 22 | + :ref:`AIOHTTPTransport <aiohttp_transport>` are more efficient for those operations. |
| 23 | + |
| 24 | +.. literalinclude:: ../code_examples/http_multipart_async.py |
| 25 | + |
| 26 | +How It Works |
| 27 | +------------ |
| 28 | + |
| 29 | +The transport sends a standard HTTP POST request with an ``Accept`` header indicating |
| 30 | +support for multipart responses: |
| 31 | + |
| 32 | +.. code-block:: text |
| 33 | +
|
| 34 | + Accept: multipart/mixed;subscriptionSpec="1.0", application/json |
| 35 | +
|
| 36 | +The server responds with a ``multipart/mixed`` content type and streams subscription |
| 37 | +updates as separate parts in the response body. Each part contains a JSON payload |
| 38 | +with GraphQL execution results. |
| 39 | + |
| 40 | +Protocol Details |
| 41 | +---------------- |
| 42 | + |
| 43 | +**Message Format** |
| 44 | + |
| 45 | +Each message part follows this structure: |
| 46 | + |
| 47 | +.. code-block:: text |
| 48 | +
|
| 49 | + --graphql |
| 50 | + Content-Type: application/json |
| 51 | +
|
| 52 | + {"payload": {"data": {...}, "errors": [...]}} |
| 53 | +
|
| 54 | +**Heartbeats** |
| 55 | + |
| 56 | +Servers may send empty JSON objects (``{}``) as heartbeat messages to keep the |
| 57 | +connection alive. These are automatically filtered out by the transport. |
| 58 | + |
| 59 | +**Error Handling** |
| 60 | + |
| 61 | +The protocol distinguishes between two types of errors: |
| 62 | + |
| 63 | +- **GraphQL errors**: Returned within the ``payload`` property alongside data |
| 64 | +- **Transport errors**: Returned with a top-level ``errors`` field and ``null`` payload |
| 65 | + |
| 66 | +**End of Stream** |
| 67 | + |
| 68 | +The subscription ends when the server sends the final boundary marker: |
| 69 | + |
| 70 | +.. code-block:: text |
| 71 | +
|
| 72 | + --graphql-- |
| 73 | +
|
| 74 | +Authentication |
| 75 | +-------------- |
| 76 | + |
| 77 | +Authentication works the same as with :ref:`AIOHTTPTransport <aiohttp_transport>`. |
| 78 | + |
| 79 | +Using HTTP Headers |
| 80 | +^^^^^^^^^^^^^^^^^^ |
| 81 | + |
| 82 | +.. code-block:: python |
| 83 | +
|
| 84 | + transport = HTTPMultipartTransport( |
| 85 | + url='https://SERVER_URL:SERVER_PORT/graphql', |
| 86 | + headers={'Authorization': 'Bearer YOUR_TOKEN'} |
| 87 | + ) |
| 88 | +
|
| 89 | +Using HTTP Cookies |
| 90 | +^^^^^^^^^^^^^^^^^^ |
| 91 | + |
| 92 | +.. code-block:: python |
| 93 | +
|
| 94 | + transport = HTTPMultipartTransport( |
| 95 | + url=url, |
| 96 | + cookies={"session_id": "your_session_cookie"} |
| 97 | + ) |
| 98 | +
|
| 99 | +Or use a cookie jar to save and reuse cookies: |
| 100 | + |
| 101 | +.. code-block:: python |
| 102 | +
|
| 103 | + import aiohttp |
| 104 | +
|
| 105 | + jar = aiohttp.CookieJar() |
| 106 | + transport = HTTPMultipartTransport( |
| 107 | + url=url, |
| 108 | + client_session_args={'cookie_jar': jar} |
| 109 | + ) |
| 110 | +
|
| 111 | +Configuration |
| 112 | +------------- |
| 113 | + |
| 114 | +Timeout Settings |
| 115 | +^^^^^^^^^^^^^^^^ |
| 116 | + |
| 117 | +Set a timeout for the HTTP request: |
| 118 | + |
| 119 | +.. code-block:: python |
| 120 | +
|
| 121 | + transport = HTTPMultipartTransport( |
| 122 | + url='https://SERVER_URL/graphql', |
| 123 | + timeout=30 # 30 second timeout |
| 124 | + ) |
| 125 | +
|
| 126 | +SSL Configuration |
| 127 | +^^^^^^^^^^^^^^^^^ |
| 128 | + |
| 129 | +Control SSL certificate verification: |
| 130 | + |
| 131 | +.. code-block:: python |
| 132 | +
|
| 133 | + transport = HTTPMultipartTransport( |
| 134 | + url='https://SERVER_URL/graphql', |
| 135 | + ssl=False # Disable SSL verification (not recommended for production) |
| 136 | + ) |
| 137 | +
|
| 138 | +Or provide a custom SSL context: |
| 139 | + |
| 140 | +.. code-block:: python |
| 141 | +
|
| 142 | + import ssl |
| 143 | +
|
| 144 | + ssl_context = ssl.create_default_context() |
| 145 | + ssl_context.load_cert_chain('client.crt', 'client.key') |
| 146 | +
|
| 147 | + transport = HTTPMultipartTransport( |
| 148 | + url='https://SERVER_URL/graphql', |
| 149 | + ssl=ssl_context |
| 150 | + ) |
| 151 | +
|
| 152 | +Limitations |
| 153 | +----------- |
| 154 | + |
| 155 | +- This transport requires the server to implement the multipart subscription protocol |
| 156 | +- Long-lived connections may be terminated by intermediate proxies or load balancers |
| 157 | +- Some server configurations may not support HTTP/1.1 chunked transfer encoding required for streaming |
| 158 | + |
| 159 | +.. _multipart subscription protocol: https://www.apollographql.com/docs/graphos/routing/operations/subscriptions/multipart-protocol |
0 commit comments