When would you run Apollo Client 4 subscriptions as multipart HTTP through HttpLink instead of a GraphQLWsLink WebSocket, and what does it cost?
answer
- no second transport to run
- the terminating HttpLink already does it
- an Accept header asks for parts
- one streaming response per subscription
- the server must speak it too
basics
~20 sWhen the server supports multipart subscriptions, HttpLink streams them with no extra library, split or connectionParams, reusing the HTTP auth headers. The cost is one long-lived HTTP response per subscription, a one-way channel, and a server that must implement the protocol.
solid answer
~50 sApollo Client 4's `HttpLink` handles subscriptions without extra setup: for a subscription operation it adds `multipart/mixed;boundary=graphql;subscriptionSpec=1.0` to the `Accept` header, reads the streamed parts as they arrive and emits each one as an event, so `useSubscription` and `subscribeToMore` work unchanged. Choose it when the server supports this protocol and you would rather not run a second transport: no `graphql-ws` dependency, no split, and authentication rides on the headers `SetContextLink` already adds, read when each subscription starts. Unsubscribing aborts the underlying fetch. The costs: every active subscription holds its own streaming response, which matters under HTTP/1.1's small per-origin connection limit; the channel is one-way; errors the server reports about the stream arrive as `CombinedProtocolErrors`; and the endpoint must implement the multipart subscription protocol. Many concurrent subscriptions per page, or a server that only speaks `graphql-ws`, still favour a WebSocket.
code
ts · 15 linesimport { ApolloClient, ApolloLink, HttpLink, InMemoryCache } from "@apollo/client";
import { SetContextLink } from "@apollo/client/link/context";
import { getAccessToken } from "./auth";
const authLink = new SetContextLink(({ headers }) => ({
headers: { ...headers, authorization: `Bearer ${getAccessToken()}` },
}));
export const client = new ApolloClient({
link: ApolloLink.from([
authLink,
new HttpLink({ uri: "https://support.example.com/graphql" }),
]),
cache: new InMemoryCache(),
});go deeper
Recall that Apollo Client 4's HttpLink can carry subscriptions itself when the server supports multipart responses, without graphql-ws.
Explain the mechanism: the Accept header HttpLink adds for subscriptions, the streamed parts becoming events, and the aborted fetch as teardown.
Choose between the transports for a real page: count concurrent streams against connection limits, compare auth paths, and confirm what the server supports.
Weigh running one transport against two across the whole platform, including proxy support, observability and how credentials are revoked on long-lived streams.
## Two ways Apollo Client carries a subscription Apollo Client 4 can deliver subscription events over two transports. The hooks do not care which one is used; the choice is made in the link chain. | | WebSocket via `GraphQLWsLink` | Multipart HTTP via `HttpLink` | |---|---|---| | Extra dependency | `graphql-ws` | none | | Link setup | a split by operation type | the existing `HttpLink` | | Authentication | `connectionParams`, read per connection | request headers, read per subscription request | | Connections | one socket shared by all subscriptions | one streaming response per subscription | | Direction | both ways | server to client | | Teardown | the operation is completed on the socket | the fetch is aborted | | Server requirement | the `graphql-ws` protocol | the multipart subscription protocol | ## How the multipart path works 1. A component calls `useSubscription(MESSAGE_ADDED, { variables: { roomId } })`. 2. The operation travels down the link chain; header-setting links such as `SetContextLink` run as they do for queries. 3. `HttpLink` sees a subscription operation and adds `multipart/mixed;boundary=graphql;subscriptionSpec=1.0` at the front of the `Accept` header. 4. A server that supports the protocol answers with a `multipart/mixed` response and keeps it open, writing one part per event. 5. `HttpLink` reads the body as a stream and emits each part as a result, which reaches the hook exactly as a WebSocket event would. 6. When the component unmounts, Apollo unsubscribes, and `HttpLink` aborts the fetch, closing the stream. No configuration switches this on; it is how `HttpLink` treats subscription operations. The server has to implement the protocol for it to work. ## What happens when the server does not support it `HttpLink` checks the response's content type. Only a `multipart/mixed` response is read as a stream; anything else is parsed as one ordinary GraphQL result, emitted once, and the observable completes. A server without multipart subscription support therefore produces a single result or an error, and a subscription that ends at once rather than one that hangs. Checking the response's content type in the network panel is the quickest way to tell which case you are in. ## When multipart HTTP is the better fit - **One transport to operate.** Proxies, load balancers, logging and rate limits already handle the app's HTTP traffic; there is no long-lived socket to add to that list. - **Auth stays uniform.** The same header logic that authenticates queries authenticates subscriptions, with no separate `connectionParams` path. - **A few, focused subscriptions.** A notification badge and one open chat room are two streams, well within what a browser handles comfortably. - **No socket-specific failure modes.** A failed stream is a failed request, visible like any other request. ## What it costs - **One response per subscription.** Each active subscription holds its own streaming HTTP response. Over HTTP/1.1 browsers cap concurrent connections per origin at a handful, so a page with many live subscriptions can starve its own queries. HTTP/2 multiplexes streams and eases this. - **One direction only.** The client cannot send anything on an open stream; every new subscription or change of variables is a new request. - **Server support is required.** An endpoint that speaks only `graphql-ws` will not stream anything back. - **Its own error channel.** Errors the server reports about the stream itself, rather than inside an event's GraphQL result, arrive as `CombinedProtocolErrors`, which you check with `CombinedProtocolErrors.is(error)`. - **Long-lived credentials.** A stream started with a token keeps running until it is closed; the header was read once, at the start. ## Deciding between them For the support console, a badge plus one open room over a server that already supports multipart subscriptions, multipart HTTP removes a dependency and a split and keeps auth in one place. A dashboard watching dozens of rooms at once, or a server that only offers `graphql-ws`, points to `GraphQLWsLink`. Both can coexist: the split can send some subscriptions to the WebSocket and let others fall through to `HttpLink`. ## Common misconceptions - That subscriptions over HTTP need a special link or extra package in Apollo Client 4. - That `HttpLink` falls back to polling when the server does not stream. - That multipart subscriptions share one response the way WebSocket subscriptions share one socket; each has its own.
- How does an Apollo Client 4 multipart subscription end when its component unmounts?Unmounting unsubscribes the operation's observable, and `HttpLink` responds by aborting the fetch through its `AbortController`, which closes the streaming response. There is no separate complete message as on a WebSocket; closing the request is the teardown.
- In Apollo Client 4, how do you tell a multipart stream error from a GraphQL error in an event?A GraphQL error inside an event's result is a `CombinedGraphQLErrors`. An error the server reports about the stream itself, outside any event's result, is a `CombinedProtocolErrors`. Both classes have a static `is()` for narrowing, and both expose the formatted errors on `errors`.
saying these in an interview costs you the question
- Subscriptions over HTTP in Apollo Client 4 need an extra link package.
- HttpLink falls back to polling when the server cannot stream a subscription.
- Multipart subscriptions share one response the way WebSocket subscriptions share one socket.
- Any GraphQL server streams subscriptions once HttpLink asks for multipart.
- SetContextLink headers cannot authenticate a multipart HTTP subscription.