09
Relay vs Apollo: two answers to the same problem
Both are mature GraphQL clients with normalized caches, Suspense hooks and optimistic updates. The difference is philosophy. Apollo is a runtime library you configure. Relay puts a compiler in front of the runtime and asks your schema to follow a few conventions, and in exchange it can do work Apollo leaves to you. Compared against Relay 21 and Apollo Client 4.3; pick a row to see both sides in code.
What you get out of the box · 14 capabilities
built in, on by defaultbuilt in, opt-inseparate toolingyou write itnot available
Relay 21
9
5
Apollo Client 4
5
3
5
1
CapabilityRelayApollo
Generated pagination queries
No first-party Apollo equivalentRelay✓on by default
@refetchable + @connection: the compiler writes the pagination query, and loadNext merges edges into the connection record.
fragment Feed_query on Query
@refetchable(queryName: "FeedPaginationQuery") {
feed(first: $count, after: $cursor) @connection(key: "Feed_feed") { ... }
}
const { data, loadNext } = usePaginationFragment(fragment, ref);Apollo Client✎you write it
Call fetchMore with the right variables and register a field policy that merges pages (relayStylePagination helps).
new InMemoryCache({
typePolicies: { Query: { fields: { feed: relayStylePagination() } } },
});
fetchMore({ variables: { after: data.feed.pageInfo.endCursor } });where Apollo is the easier choice
- No compiler or build step: add the client and start writing queries.
- Works with any GraphQL schema. Relay works best when the server follows the Node interface and connection spec.
- Easy to adopt incrementally in an existing app, one query at a time.
- A larger ecosystem: Apollo Link middleware, the Router/GraphOS platform, plenty of tutorials.
- Flexible cache policies (typePolicies, keyFields) for schemas without global ids.
what both do well
- A normalized cache keyed by entity id (Relay: global id; Apollo: __typename + id or keyFields).
- Optimistic responses that roll back on error.
- Suspense-first hooks and query preloading before render.
- @defer and @stream against spec-compatible servers.
- SSR and Next.js App Router support.
- why it mattersRelay's edge comes from knowing every query at build time: validation, types, persisted ids, generated pagination and masking all fall out of that.
- why it mattersApollo has been closing gaps (data masking,
useFragment, @defer/@stream), usually as opt-in features on top of a flexible runtime. - why it mattersRelay asks more of the schema (Node, connections, mutation payloads). If you control the server, that's cheap. If you don't, Apollo's flexibility matters more.