Apollo: The GraphQL Platform
Meteor Development Group launched Apollo Client in 2016, just months after Facebook open-sourced GraphQL. Where Facebook had released a spec and a reference server implementation, Apollo built the full stack: a JavaScript client for React, Angular, and Vue; a server implementation; developer tooling; and eventually a cloud platform. Apollo made GraphQL production-ready for teams that did not have Facebook-scale resources.
Apollo Client introduced normalized client-side caching. Responses were normalized by object ID, so if the same user appeared in five queries, they were stored once and every query shared the same reference. Update the user in one query, and all components reading that user re-rendered. This made the client cache behave like a local database that automatically stayed in sync with the server.
GraphQL Subscriptions: Real-Time Data
GraphQL Subscriptions added real-time to the query model. Instead of a query that executes once and returns, a subscription maintains a live connection and pushes updates when the subscribed data changes. The query shape is identical to a regular query - same syntax, same fields, same type system. The transport is typically WebSocket rather than HTTP.
Subscriptions made GraphQL viable for chat applications, live dashboards, collaborative editing, and any feature that traditionally required polling or WebSocket custom protocols. The ability to use the same type-safe query language for both request-response and real-time data was a significant ergonomic improvement over maintaining separate REST and WebSocket code.
Apollo Federation: GraphQL at Scale
Apollo Federation (2019) solved the organizational problem that microservices create for GraphQL. In a single team, one GraphQL schema is manageable. At a company with 50 teams, maintaining a single schema becomes a coordination nightmare. Federation lets each team own their subgraph - a GraphQL service with its own schema. The federation gateway stitches these into a single unified graph at runtime.
Federation's Entity concept is the key innovation: an object type can be defined in one subgraph and extended by another. The User entity might be defined by the auth team's subgraph and extended by the orders team's subgraph with additional fields. From the client's perspective, it's one User type. Ownership is distributed. The interface is unified.
When to Choose GraphQL vs REST
GraphQL adds real value when: clients are diverse (mobile, web, third-party) and have different data needs; your data has deep relationships that REST struggles to express in reasonable round trips; you want strong typing enforced at the API boundary. REST remains simpler when: your API is straightforward CRUD; you have simple caching requirements; your clients are few and uniform; or your team is not yet familiar with GraphQL resolver patterns.
Subscriptions vs. Polling vs. Server-Sent Events
GraphQL subscriptions are one of three real-time patterns, and choosing correctly matters. Polling: the client sends a query on a timer and processes any changed data. Simple, stateless, works everywhere, wastes bandwidth when nothing changes. Server-Sent Events (SSE): a unidirectional HTTP stream from server to client. Simpler than WebSockets, works over HTTP/2, good for live feeds where the client only reads. WebSocket subscriptions: bidirectional, supports the full subscribe/unsubscribe interaction model, higher connection overhead.
GraphQL subscriptions use WebSockets by default because the subscribe/unsubscribe model requires bidirectionality. But many subscription use cases (live dashboards, notification feeds, order status updates) are purely server-to-client. SSE via GraphQL is a viable alternative that avoids WebSocket connection management overhead. The graphql-sse library implements GraphQL subscriptions over Server-Sent Events and is simpler to deploy behind standard HTTP infrastructure than WebSocket endpoints.
Federation's Impact on Team Autonomy
Apollo Federation is primarily an organizational solution, not a technical one. The technical problem (composing GraphQL schemas) has other solutions (schema stitching, gateway merging). Federation's real value is that it assigns clear ownership: the team that owns users owns the User subgraph. The team that owns orders extends User with order-related fields. Changes to the User subgraph do not require coordination with the orders team as long as the entity interface is preserved. Teams can deploy their subgraphs independently and the gateway updates the supergraph dynamically.
Federation also exposes organizational problems. If you cannot clearly assign ownership of each type to a team, your service boundaries are probably wrong. The exercise of designing a federated schema is a forcing function for clarifying ownership. Teams that go through this process often discover that their service boundaries were drawn around implementation convenience rather than business domain boundaries. Federation makes the organizational structure visible in the API layer.
- Apollo Federation 2 (2022) improved on Federation 1 with better support for shared types, value types (types not owned by a single subgraph), and progressive migration.
- The @key directive in Federation marks the field(s) that uniquely identify an entity across subgraphs. Usually this is id, but compound keys are supported.
- Subscription scalability requires a pub/sub layer (Redis Pub/Sub, Apache Kafka) between subscription resolvers and clients. A subscription connection is not stateless - it persists for the life of the subscription.
- Apollo Client's reactive cache means UI components re-render automatically when cache data changes - even when the change came from a mutation by a different component on the same page.
- GraphQL persisted operations are production-critical for preventing arbitrary query injection from public clients.
The Operational Reality of Running GraphQL at Scale
GraphQL in production requires operational investments that REST APIs do not. Query complexity analysis must be implemented to prevent runaway queries that join deeply nested relationships - a malicious or buggy query can bring down a GraphQL server with a single request if depth and complexity limits are not enforced. Apollo's query depth limit and cost analysis are standard mitigations. Persisted operations (clients send a query ID instead of the full query text) are required for production systems to prevent arbitrary query injection and enable server-side optimization.
Observability for GraphQL is different from REST. Traditional API monitoring measures latency and error rate per endpoint. GraphQL has one endpoint. Useful monitoring requires tracking per-operation metrics: which operations are slowest, which operations are most frequent, which fields are most used (to guide deprecation decisions), and which resolver functions are the N+1 bottlenecks. Apollo Studio and Stellate provide this visibility. Implementing it yourself requires resolver-level tracing middleware and a metrics aggregation pipeline.
Client-Side Cache Management in Production
Apollo Client's normalized cache is powerful but requires explicit management in production applications. After a mutation that creates a new item, the cache does not automatically know to update list queries that should now include the new item. You have three options: refetch the list query after the mutation (simple, correct, an extra network request), manually update the cache to add the new item (complex, avoids the network request), or use Apollo's cache policies (optimistic responses that update the UI before the server confirms). The choice depends on how critical real-time accuracy is for your UI. Most applications use refetchQueries for simplicity and optimistic updates for the few interactions where perceived speed matters most.
Subscriptions vs. Polling vs. Server-Sent Events
GraphQL subscriptions are one of three real-time patterns, and choosing correctly matters. Polling: the client sends a query on a timer and processes any changed data. Simple, stateless, works everywhere, wastes bandwidth when nothing changes. Server-Sent Events (SSE): a unidirectional HTTP stream from server to client. Simpler than WebSockets, works over HTTP/2, good for live feeds where the client only reads. WebSocket subscriptions: bidirectional, supports the full subscribe/unsubscribe interaction model, higher connection overhead.
GraphQL subscriptions use WebSockets by default because the subscribe/unsubscribe model requires bidirectionality. But many subscription use cases (live dashboards, notification feeds, order status updates) are purely server-to-client. SSE via GraphQL is a viable alternative that avoids WebSocket connection management overhead. The graphql-sse library implements GraphQL subscriptions over Server-Sent Events and is simpler to deploy behind standard HTTP infrastructure than WebSocket endpoints.
Federation's Impact on Team Autonomy
Apollo Federation is primarily an organizational solution, not a technical one. The technical problem (composing GraphQL schemas) has other solutions (schema stitching, gateway merging). Federation's real value is that it assigns clear ownership: the team that owns users owns the User subgraph. The team that owns orders extends User with order-related fields. Changes to the User subgraph do not require coordination with the orders team as long as the entity interface is preserved. Teams can deploy their subgraphs independently and the gateway updates the supergraph dynamically.
Federation also exposes organizational problems. If you cannot clearly assign ownership of each type to a team, your service boundaries are probably wrong. The exercise of designing a federated schema is a forcing function for clarifying ownership. Teams that go through this process often discover that their service boundaries were drawn around implementation convenience rather than business domain boundaries. Federation makes the organizational structure visible in the API layer.
- Apollo Federation 2 (2022) improved on Federation 1 with better support for shared types, value types (types not owned by a single subgraph), and progressive migration.
- The @key directive in Federation marks the field(s) that uniquely identify an entity across subgraphs. Usually this is id, but compound keys are supported.
- Subscription scalability requires a pub/sub layer (Redis Pub/Sub, Apache Kafka) between subscription resolvers and clients. A subscription connection is not stateless - it persists for the life of the subscription.
- Apollo Client's reactive cache means UI components re-render automatically when cache data changes - even when the change came from a mutation by a different component on the same page.
- GraphQL persisted operations are production-critical for preventing arbitrary query injection from public clients.
The Operational Reality of Running GraphQL at Scale
GraphQL in production requires operational investments that REST APIs do not. Query complexity analysis must be implemented to prevent runaway queries that join deeply nested relationships - a malicious or buggy query can bring down a GraphQL server with a single request if depth and complexity limits are not enforced. Apollo's query depth limit and cost analysis are standard mitigations. Persisted operations (clients send a query ID instead of the full query text) are required for production systems to prevent arbitrary query injection and enable server-side optimization.
Observability for GraphQL is different from REST. Traditional API monitoring measures latency and error rate per endpoint. GraphQL has one endpoint. Useful monitoring requires tracking per-operation metrics: which operations are slowest, which operations are most frequent, which fields are most used (to guide deprecation decisions), and which resolver functions are the N+1 bottlenecks. Apollo Studio and Stellate provide this visibility. Implementing it yourself requires resolver-level tracing middleware and a metrics aggregation pipeline.
Client-Side Cache Management in Production
Apollo Client's normalized cache is powerful but requires explicit management in production applications. After a mutation that creates a new item, the cache does not automatically know to update list queries that should now include the new item. You have three options: refetch the list query after the mutation (simple, correct, an extra network request), manually update the cache to add the new item (complex, avoids the network request), or use Apollo's cache policies (optimistic responses that update the UI before the server confirms). The choice depends on how critical real-time accuracy is for your UI. Most applications use refetchQueries for simplicity and optimistic updates for the few interactions where perceived speed matters most.
Subscriptions vs. Polling vs. Server-Sent Events
GraphQL subscriptions are one of three real-time patterns, and choosing correctly matters. Polling: the client sends a query on a timer and processes any changed data. Simple, stateless, works everywhere, wastes bandwidth when nothing changes. Server-Sent Events (SSE): a unidirectional HTTP stream from server to client. Simpler than WebSockets, works over HTTP/2, good for live feeds where the client only reads. WebSocket subscriptions: bidirectional, supports the full subscribe/unsubscribe interaction model, higher connection overhead.
GraphQL subscriptions use WebSockets by default because the subscribe/unsubscribe model requires bidirectionality. But many subscription use cases (live dashboards, notification feeds, order status updates) are purely server-to-client. SSE via GraphQL is a viable alternative that avoids WebSocket connection management overhead. The graphql-sse library implements GraphQL subscriptions over Server-Sent Events and is simpler to deploy behind standard HTTP infrastructure than WebSocket endpoints.
Federation's Impact on Team Autonomy
Apollo Federation is primarily an organizational solution, not a technical one. The technical problem (composing GraphQL schemas) has other solutions (schema stitching, gateway merging). Federation's real value is that it assigns clear ownership: the team that owns users owns the User subgraph. The team that owns orders extends User with order-related fields. Changes to the User subgraph do not require coordination with the orders team as long as the entity interface is preserved. Teams can deploy their subgraphs independently and the gateway updates the supergraph dynamically.
Federation also exposes organizational problems. If you cannot clearly assign ownership of each type to a team, your service boundaries are probably wrong. The exercise of designing a federated schema is a forcing function for clarifying ownership. Teams that go through this process often discover that their service boundaries were drawn around implementation convenience rather than business domain boundaries. Federation makes the organizational structure visible in the API layer.
- Apollo Federation 2 (2022) improved on Federation 1 with better support for shared types, value types (types not owned by a single subgraph), and progressive migration.
- The @key directive in Federation marks the field(s) that uniquely identify an entity across subgraphs. Usually this is id, but compound keys are supported.
- Subscription scalability requires a pub/sub layer (Redis Pub/Sub, Apache Kafka) between subscription resolvers and clients. A subscription connection is not stateless - it persists for the life of the subscription.
- Apollo Client's reactive cache means UI components re-render automatically when cache data changes - even when the change came from a mutation by a different component on the same page.
- GraphQL persisted operations are production-critical for preventing arbitrary query injection from public clients.
The Operational Reality of Running GraphQL at Scale
GraphQL in production requires operational investments that REST APIs do not. Query complexity analysis must be implemented to prevent runaway queries that join deeply nested relationships - a malicious or buggy query can bring down a GraphQL server with a single request if depth and complexity limits are not enforced. Apollo's query depth limit and cost analysis are standard mitigations. Persisted operations (clients send a query ID instead of the full query text) are required for production systems to prevent arbitrary query injection and enable server-side optimization.
Observability for GraphQL is different from REST. Traditional API monitoring measures latency and error rate per endpoint. GraphQL has one endpoint. Useful monitoring requires tracking per-operation metrics: which operations are slowest, which operations are most frequent, which fields are most used (to guide deprecation decisions), and which resolver functions are the N+1 bottlenecks. Apollo Studio and Stellate provide this visibility. Implementing it yourself requires resolver-level tracing middleware and a metrics aggregation pipeline.
Client-Side Cache Management in Production
Apollo Client's normalized cache is powerful but requires explicit management in production applications. After a mutation that creates a new item, the cache does not automatically know to update list queries that should now include the new item. You have three options: refetch the list query after the mutation (simple, correct, an extra network request), manually update the cache to add the new item (complex, avoids the network request), or use Apollo's cache policies (optimistic responses that update the UI before the server confirms). The choice depends on how critical real-time accuracy is for your UI. Most applications use refetchQueries for simplicity and optimistic updates for the few interactions where perceived speed matters most.
