Reducing Perceived Latency with the @defer Directive in Apollo GraphQL
Stop letting slow resolvers block your entire UI. Learn how to use the @defer directive in Apollo GraphQL to stream partial results and improve perceived performance.
29 Nov 2025, 21:27 UTC

The "All-or-Nothing" Response Problem
In a standard GraphQL execution, the server waits for every single requested field to resolve before sending the response back to the client. If your query requests a fast-loading user profile but also a slow-loading analytics report from a legacy system, the user sees a loading spinner for the duration of the slowest field. This creates a bottleneck where the fastest data is held hostage by the slowest data.
The @defer directive solves this by allowing the server to send an initial response containing the fast data immediately, while "deferring" the expensive fields. These deferred fields are streamed to the client as they resolve, allowing the UI to render progressively rather than waiting for a complete payload.
How @defer Changes the Data Flow
When a client marks a fragment with @defer, the server changes how it communicates over the network. Instead of a single JSON object, it uses a multipart HTTP response. The server sends the initial payload, keeps the connection open, and then pushes additional "patches" containing the deferred data once the resolvers finish.
This is particularly effective in Apollo Federation 2.0+, where a single query might span multiple subgraphs. If one subgraph is experiencing high latency, @defer prevents that specific service from blocking the rest of the gateway's response.
Implementation Example: Progressive User Dashboard
Consider a dashboard that needs basic user info (fast) and a complex set of recommendations based on ML models (slow). Without @defer, the page is blank until the ML model responds.
The Query:
Run this in your GraphQL client or Apollo Studio Explorer to test the behavior.
query GetUserDashboard($userId: ID!) {
user(id: $userId) {
id
username
email
# This fragment is deferred and will arrive later
... @defer(label: "recommendations") {
recommendations {
productId
productName
score
}
}
}
}Execution Details:
- Server Requirement: Apollo Server 3.12+ or Apollo Router.
- Transport: Must be run over HTTP/2 or WebSockets. Standard HTTP/1.1 does not support the necessary streaming response headers.
- Permissions: Ensure the gateway/router has
@deferenabled in its configuration, as some production environments disable streaming by default to simplify load balancer settings.
Verifying the Stream
To verify @defer is working, open your browser's Network tab. Instead of a single response that finishes in 2 seconds, you should see a response that stays "Pending" or "Downloading" while the page content populates in stages. The response body will contain multiple JSON objects, each with a path array indicating where the deferred data fits into the original query structure.
Trade-offs and Infrastructure Constraints
While @defer improves perceived performance, it introduces technical overhead that may not be suitable for every project:
| Constraint | Impact |
|---|---|
| Transport Layer | Requires HTTP/2. If your load balancer or proxy (like some older Nginx configs) strips chunked encoding or forces HTTP/1.1, the response will revert to a standard blocking call. |
| Client Logic | Standard JSON parsers cannot handle multipart responses. You must use a client library capable of handling streams (like certain versions of Apollo Client or custom multipart handlers). |
| Caching | CDN caching becomes complex. Since the response is a stream of multiple parts, traditional edge caching of the entire HTTP response is often impossible. |
Practical Decision: When to use @defer
Avoid using @defer for every single field; this creates unnecessary network chatter. Instead, apply it to fields that meet these criteria:
- The field relies on a third-party API or a slow database query.
- The UI can meaningfully render the rest of the page without this data (e.g., a "Loading..." skeleton state).
- The data is not required for the primary action of the page.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.