<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: WunderGraph</title>
    <description>The latest articles on Forem by WunderGraph (@wundergraph).</description>
    <link>https://forem.com/wundergraph</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3854287%2Fb07c1ad4-cd45-47b1-989f-1003b77dbf6b.png</url>
      <title>Forem: WunderGraph</title>
      <link>https://forem.com/wundergraph</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wundergraph"/>
    <language>en</language>
    <item>
      <title>The Three Bottlenecks AI Can't Code Away</title>
      <dc:creator>WunderGraph</dc:creator>
      <pubDate>Thu, 07 May 2026 10:02:10 +0000</pubDate>
      <link>https://forem.com/wundergraph/the-three-bottlenecks-ai-cant-code-away-5cei</link>
      <guid>https://forem.com/wundergraph/the-three-bottlenecks-ai-cant-code-away-5cei</guid>
      <description>&lt;p&gt;TL;DR&lt;/p&gt;

&lt;p&gt;AI writes nearly half of all new code, but teams aren't shipping faster. The bottleneck moved from writing code to coordination, governance, and review. GraphQL Federation addresses all three with clear ownership, async governance, and schema-level usage tracking.&lt;/p&gt;




&lt;p&gt;AI now generates roughly 40–45% of all new code, according to recent developer surveys on AI-assisted coding &lt;a href="https://www.sonarsource.com/the-state-of-code/developer-survey-report/" rel="noopener noreferrer"&gt;(Sonar State of Code Developer Survey )&lt;/a&gt;. Teams with high AI adoption have merged &lt;a href="https://www.faros.ai/blog/ai-acceleration-whiplash-takeaways" rel="noopener noreferrer"&gt;nearly 2× more pull requests  year over year&lt;/a&gt;. But PR review times have increased by around 90% in some organizations, change failure rates are up, and deployment frequency often hasn’t caught up (&lt;a href="https://www.faros.ai/blog/ai-software-engineering" rel="noopener noreferrer"&gt;Faros AI: The AI Productivity Paradox Report 2025&lt;/a&gt; ).&lt;/p&gt;

&lt;p&gt;We have a 10x faster engine bolted onto a 1x organization.&lt;/p&gt;

&lt;p&gt;Unfortunately, the bottleneck didn't disappear. It moved.&lt;/p&gt;

&lt;p&gt;If you're leading an engineering org of 50, 100, or 500 people and wondering where the productivity gains went, this post is for you. It addresses the three things your AI tools can't solve: &lt;strong&gt;coordination, governance, and review.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why AI Coding Tools Aren't Making Engineering Teams Faster
&lt;/h3&gt;

&lt;p&gt;Every engineering leader I talk to has the same story.&lt;/p&gt;

&lt;p&gt;They rolled out some internal AI tooling, like Copilot or Cursor, and their engineers are writing more code than ever. Those metrics look great on paper.&lt;/p&gt;

&lt;p&gt;But features aren't shipping faster.&lt;/p&gt;

&lt;p&gt;The reason is simple: &lt;strong&gt;writing code was never the biggest bottleneck.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, coding took time. But when you had a clear spec, it was fast. The real time sink was always the work around the code: figuring out what to build, where it lives, who needs to agree, and whether the result actually meets the spec.&lt;/p&gt;

&lt;p&gt;AI made the coding part nearly instant. Turning a spec into code is fast now. And that has exposed how slow everything around it is. You have a spec, implement part of it, hit a new problem, go back to planning, and then you need to re-coordinate.&lt;/p&gt;

&lt;p&gt;That loop is where the time actually goes. AI helps you get through the implementation steps faster, but you barely save any time if every return to planning means more meetings and governance overhead.&lt;/p&gt;

&lt;p&gt;Think of it like a highway. You widened the middle section from two lanes to ten. But the on and off-ramps are still one lane. Traffic didn't improve. It just moved to the ramps.&lt;/p&gt;

&lt;p&gt;The three ramps are coordination, governance, and review.&lt;/p&gt;

&lt;h3&gt;
  
  
  How API Coordination Becomes the Bottleneck in Large Engineering Orgs
&lt;/h3&gt;

&lt;p&gt;Here's a scenario I see constantly.&lt;/p&gt;

&lt;p&gt;A 70-person engineering org. Ten teams, six to eight engineers each. A product manager creates a ticket: "Add listening history to the user profile." Simple feature. Should take a few days.&lt;/p&gt;

&lt;p&gt;It takes three weeks.&lt;/p&gt;

&lt;p&gt;It doesn't take three weeks to build it; it takes three weeks to figure out where it goes.&lt;/p&gt;

&lt;p&gt;Which team owns the user profile? Which service holds listening history? Do we add it to an existing endpoint or create a new one?&lt;/p&gt;

&lt;p&gt;The frontend engineer Slack's the platform team. The platform team sets up a meeting with the backend team. The backend team informs them that it is actually two services, owned by two different teams. More meetings.&lt;/p&gt;

&lt;p&gt;Three weeks later, they have a spec. The code takes a day.&lt;/p&gt;

&lt;p&gt;This is the coordination tax, and it scales linearly with org size. More teams, more services, more time spent figuring out who to talk to.&lt;/p&gt;

&lt;p&gt;AI doesn't help here because you can't prompt your way out of an organizational problem.&lt;/p&gt;

&lt;p&gt;With REST APIs, this is structural. Every URL lives on a specific server, owned by a specific team. The moment you design an endpoint, you're making a backend decision. With 50 microservices, you have 50 possible homes for every new feature and no clear way to navigate them.&lt;/p&gt;

&lt;p&gt;Compare this to &lt;a href="https://wundergraph.com/blog/a-brief-overview-of-open-source-graphql-federation" rel="noopener noreferrer"&gt;GraphQL Federation&lt;/a&gt;. In a federated graph, you don't start with the backend. You start with the customer. What does the API consumer need? You &lt;a href="https://wundergraph.com/blog/dream-query-design-apis-from-consumer-out" rel="noopener noreferrer"&gt;model the perfect query&lt;/a&gt;, the "dream query", without thinking about which service implements it.&lt;/p&gt;

&lt;p&gt;Then the supergraph tells you. It shows you which teams own the relevant entities, which subgraphs already implement parts of what you need, and where the gaps are.&lt;/p&gt;

&lt;p&gt;Do you want to talk to 10 teams or let the graph tell you which 3 people to talk to?&lt;/p&gt;

&lt;h3&gt;
  
  
  API Governance at Scale: Why Catalogs and OpenAPI Specs Fall Short
&lt;/h3&gt;

&lt;p&gt;Coordination answers who to talk to. Governance answers what's allowed.&lt;/p&gt;

&lt;p&gt;Once you've figured out where a feature lives, someone still needs to decide if it's the right design. Is the naming consistent? Does it follow the schema conventions? Does it overlap with something another team already built?&lt;/p&gt;

&lt;p&gt;In most organizations, this is a monthly API review committee. A queue of proposals waiting for the platform team to review. Product teams are blocked on a meeting that happens in two weeks.&lt;/p&gt;

&lt;p&gt;This doesn't scale.&lt;/p&gt;

&lt;p&gt;The deeper problem is that traditional API tools don't give you a domain-level view.&lt;/p&gt;

&lt;p&gt;An API catalog is a list of endpoints. It tells you "here's a URL." It doesn't tell you the story of the data. What a playlist is, where it comes from, who owns each part.&lt;/p&gt;

&lt;p&gt;Imagine a supergraph with 100,000 lines of SDL. That sounds unmanageable, but it's not. Because you don't navigate it by URL. You navigate it by entity.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Playlist&lt;/code&gt; entity might have 100 fields coming from 20 different services. In a graph, you see all of them in one place: the fields, the ownership, the relationships. You see that &lt;code&gt;Playlist.tracks&lt;/code&gt; comes from the catalog service, &lt;code&gt;Playlist.recommendations&lt;/code&gt; comes from the ML team, and &lt;code&gt;Playlist.analytics&lt;/code&gt; comes from the data platform.&lt;/p&gt;

&lt;p&gt;Try doing that with an&lt;a href="https://spec.openapis.org/oas/latest.html" rel="noopener noreferrer"&gt; OpenAPI spec&lt;/a&gt; . You'd be reading 20 different API documents with no way to see how they relate.&lt;/p&gt;

&lt;p&gt;Endpoints don't tell a story about the data. Entities do.&lt;/p&gt;

&lt;p&gt;This is exactly what &lt;a href="https://wundergraph.com/hub" rel="noopener noreferrer"&gt;WunderGraph Hub&lt;/a&gt;  is built for. Entities and their ownership are first-class concepts, not a wiki page someone forgot to update.&lt;/p&gt;

&lt;p&gt;You see every entity, who owns it, and which services contribute to it. Governance becomes self-service: you propose a change, Hub tells you who's affected, and those people review it asynchronously. No committee meeting. No queue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why AI-Generated Code Is Slowing Down Code Review
&lt;/h3&gt;

&lt;p&gt;AI generates code fast. But &lt;a href="https://events.sonarsource.com/2026-state-of-code-developer-survey/" rel="noopener noreferrer"&gt;96% of developers&lt;/a&gt;  don't fully trust its functional accuracy.&lt;/p&gt;

&lt;p&gt;So every AI-generated PR needs a human reviewer. And review times are &lt;a href="https://www.faros.ai/blog/ai-software-engineering" rel="noopener noreferrer"&gt;up 91%&lt;/a&gt;  because PRs are larger, more frequent, and harder to evaluate when you're not sure if the author (or the AI) truly understood the requirements.&lt;/p&gt;

&lt;p&gt;This is the third bottleneck: review.&lt;/p&gt;

&lt;p&gt;But here's the thing. Review is only painful when the spec is weak.&lt;/p&gt;

&lt;p&gt;If you've solved bottleneck #1 (coordination) and #2 (governance), you have a clear, agreed-upon spec before anyone writes a line of code. Reviewing becomes validation against that spec, not an open-ended architecture debate in a PR comment thread.&lt;/p&gt;

&lt;p&gt;At the schema level, this gets even more concrete. With Federation, you can track &lt;a href="https://wundergraph.com/blog/graphql-schema-design-principles" rel="noopener noreferrer"&gt;field-level usage&lt;/a&gt; across all clients. When a field is deprecated, every affected client sees it immediately. When a deprecated field hits zero usage, the system tells you it's safe to remove. We call this graph pruning: sunsetting fields and APIs that are no longer needed.&lt;/p&gt;

&lt;p&gt;This is a natural lifecycle for API evolution: propose, implement, track, deprecate, and prune. No surprises. No "wait, who's still using this?"&lt;/p&gt;

&lt;p&gt;If you have a good spec, reviewing becomes much easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  How GraphQL Federation Solves Coordination, Governance, and Review
&lt;/h3&gt;

&lt;p&gt;Federation is not a better API technology. You can solve the technical problem of routing a request to 50 services with BFFs, API gateways, gRPC, or any number of tools.&lt;/p&gt;

&lt;p&gt;Federation solves the organizational problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coordination:&lt;/strong&gt; Start with the supergraph. Add the capabilities the consumer needs. Don't worry about which backend implements them yet. The graph's ownership model tells you exactly who to talk to. In &lt;a href="https://wundergraph.com/hub" rel="noopener noreferrer"&gt;WunderGraph Hub&lt;/a&gt; , creating a draft or proposal for adding a handful of fields to an entity takes a couple of minutes. Expand the graph, see the affected owners, and submit. More complex changes take longer, but the baseline is fast. No meetings. No scavenger hunts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Governance:&lt;/strong&gt; Proposals are reviewed asynchronously by the right people. Not a central committee, but the actual entity owners. They see exactly what's changing, in context, on a visual canvas that shows clients on the left, the supergraph in the middle, and services on the right. One single pane of glass for the entire API surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review:&lt;/strong&gt; Schema usage analytics, deprecation tracking, and graph pruning give you a continuous feedback loop. You know what's being used, what's stale, and what's safe to remove. Reviews are against a concrete spec, not vibes.&lt;/p&gt;

&lt;p&gt;SoundCloud is a powerful example of this in action. They &lt;a href="https://developers.soundcloud.com/blog/service-architecture-1" rel="noopener noreferrer"&gt;popularized the BFF pattern&lt;/a&gt;  in 2011, the original workaround for "how do multiple frontends talk to many backends?" For years, it worked. Then the number of frontends grew, the number of services grew, and the BFFs became their own coordination problem: a layer of indirection that hid how frontends depended on backend services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wundergraph.com/case-study/soundcloud" rel="noopener noreferrer"&gt;They're now migrating to GraphQL Federation .&lt;/a&gt; The company that popularized the workaround is adopting the real solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  When You Don't Need GraphQL Federation
&lt;/h3&gt;

&lt;p&gt;If you're a 10-person team with one frontend and a handful of services, don't adopt Federation.&lt;/p&gt;

&lt;p&gt;Build a monolith. Move fast. Ship features. Stay under 10 engineers as long as you can.&lt;/p&gt;

&lt;p&gt;Federation comes with a cost: schema design, composition, ownership models, governance workflows. That cost pays for itself when you have multiple teams, multiple frontends, and a growing service landscape.&lt;/p&gt;

&lt;p&gt;The threshold is roughly this: if adding a new feature requires coordinating across three or more teams, you have the organizational problem Federation solves. If it doesn't, you don't need it.&lt;/p&gt;

&lt;p&gt;Don't solve a problem you don't have.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Get the 10x Productivity Gain from AI
&lt;/h3&gt;

&lt;p&gt;The companies that move fastest in 2026 won't be the ones writing code fastest, but the ones getting from ticket to production fastest.&lt;/p&gt;

&lt;p&gt;The path from ticket to production has many steps: coordination, spec, implementation, review, governance, deployment. AI already made implementation fast. The question is whether you can make every other step just as fast.&lt;/p&gt;

&lt;p&gt;Federation handles ticket → spec. AI handles spec → code. Schema analytics and governance handle review. Automate every repetitive step in between, and you get something close to the 10x everyone was promised.&lt;/p&gt;

&lt;p&gt;But if you only invest in the AI side, faster code generation, smarter autocomplete, agentic coding, you're widening the highway while the on-ramp stays one lane.&lt;/p&gt;

&lt;p&gt;Smaller companies have a natural advantage here. Less coordination overhead. Fewer teams to align. Larger organizations that don't address these bottlenecks will find themselves outpaced by leaner teams who got the full pipeline right, not just the coding part.&lt;/p&gt;




&lt;p&gt;You can read the original article here: &lt;a href="https://wundergraph.com/blog/ai-coding-bottlenecks" rel="noopener noreferrer"&gt;https://wundergraph.com/blog/ai-coding-bottlenecks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Where Does API Complexity Live?</title>
      <dc:creator>WunderGraph</dc:creator>
      <pubDate>Mon, 04 May 2026 14:06:27 +0000</pubDate>
      <link>https://forem.com/wundergraph/where-does-api-complexity-live-31ik</link>
      <guid>https://forem.com/wundergraph/where-does-api-complexity-live-31ik</guid>
      <description>&lt;p&gt;TL;DR&lt;/p&gt;

&lt;p&gt;Every API architecture handles the same five concerns - data fetching, security, caching, contract management, and governance. None of them disappear. They just move. REST pushes data fetching to the client and keeps security and caching in the infrastructure. GraphQL pulls data fetching to the server and pushes security and caching into application code. Federation distributes the runtime concerns across teams but lands governance on humans. Fission moves governance from humans to design-time tooling. The question you should be asking yourself is where you want each responsibility to live.&lt;/p&gt;

&lt;p&gt;In thermodynamics, the first law is non-negotiable: energy cannot be created or destroyed. It can only change form. To heat one thing, you must cool another. The total energy in a closed system remains constant.&lt;/p&gt;

&lt;p&gt;Software architecture obeys a similar law. Larry Tesler, while at Xerox PARC in the 1980s, called it the Law of Conservation of Complexity: "Every application has an inherent amount of irreducible complexity. The only question is: Who will have to deal with it?" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;1.&lt;/a&gt; Fred Brooks drew a complementary line in 1986, distinguishing essential complexity - the irreducible difficulty of the problem itself - from accidental complexity introduced by our tools &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;2.&lt;/a&gt; The essential part cannot be engineered away. It has to live somewhere.&lt;/p&gt;

&lt;p&gt;Every API architecture must handle the same set of concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data fetching:&lt;/strong&gt; who assembles data from multiple sources?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; where are access controls enforced?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching:&lt;/strong&gt; where are responses stored and invalidated?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract management:&lt;/strong&gt; who maintains the schema between producer and consumer?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance:&lt;/strong&gt; what are the rules for how the system evolves?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These concerns never disappear. They move - between client and server, between infrastructure and application code, between modules in a monolith and services in a microservices architecture, between humans and tooling.&lt;/p&gt;

&lt;p&gt;This article traces where each concern lives across four architectural approaches: REST, GraphQL, Federated GraphQL, and an emerging pattern that WunderGraph calls &lt;a href="https://wundergraph.com/blog/fission-algorithm-deep-dive" rel="noopener noreferrer"&gt;Fission&lt;/a&gt;. The goal is not to compare features or declare a winner. It is to map responsibility: &lt;em&gt;where does each concern live, and who owns it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One important caveat before we begin. REST is often discussed in its idealised form - proper HTTP caching, clean resource modelling, well-maintained OpenAPI specifications. In practice, most REST APIs do not fully achieve this. This is not just my opinion - Postman's State of the API reports consistently show gaps between API design intent and reality &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;22&lt;/a&gt;, and Fielding himself has noted that most APIs calling themselves REST do not actually conform to the architectural constraints he defined &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;25.&lt;/a&gt; That gap is not an indictment of REST. It is a recognition that when any architecture's idealised model meets operational reality, responsibility shifts. Where it shifts to is the question worth asking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do API architectures handle data from multiple services?
&lt;/h3&gt;

&lt;p&gt;You lead the engineering team for a product detail page at a large e-commerce company - the single most visited page type on your platform. The page needs product information, pricing, inventory levels, reviews, and shipping availability. This data lives across four backend services, each owned by a different team. You serve two clients: a mobile app showing a condensed view and a web storefront showing everything.&lt;/p&gt;

&lt;p&gt;The same five concerns apply regardless of architectural choice. What changes is where each one lives - and who carries it.&lt;/p&gt;

&lt;p&gt;We start with REST, where most of these responsibilities begin their journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  REST
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why does REST push data fetching to the client?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Data fetching lives with the client.&lt;/strong&gt; The product detail page requires five sequential HTTP calls - one per service. On a slow connection, each round trip adds latency. The mobile app receives 47 fields per response but uses 8. This is the aggregation problem that REST's resource model does not inherently address - what Microsoft's Azure Architecture Centre documents as the "Chatty I/O" antipattern &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;23.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Responsibility for solving it falls on someone: the client writes orchestration code, the API team builds per-consumer aggregation endpoints, or a Backend-for-Frontend layer absorbs the work. A BFF sits between the client and the backend services - the mobile app makes one call to its BFF, which fans out to all five services server-side, filters the response down to the eight fields the mobile client needs, and returns a single payload. The five round trips collapse into one. But now someone needs to build and maintain that BFF. Usually it falls to the frontend team, who now own a server-side service in addition to their client code. Alternatively, a platform team provides the BFF - but then every consumer change requires a platform team ticket. Either way, you need a BFF per client type, each with its own aggregation logic. And as backend services evolve - new fields, changed response shapes, deprecated endpoints - each BFF becomes brittle, requiring constant maintenance to stay in sync. Phil Calcado formalised the BFF pattern at SoundCloud but warned about its limits: "Trying to derive a single schema that holds a complete-ish model of your data...reminds me too much of an Enterprise Data Model" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;5.&lt;/a&gt; Martin Fowler echoed: "Beware of a One-Size-Fits-All API in any form" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;6.&lt;/a&gt; The aggregation work exists. The question is who carries it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does REST handle caching and security?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Security lives in the infrastructure layer.&lt;/strong&gt; WAFs inspect requests by URL pattern. Rate limiting works per endpoint. OAuth scopes map to URL paths. Decades of HTTP security tooling understand this model natively. This is one area where REST's operational reality aligns closely with its idealised form - the infrastructure is mature and widely deployed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching lives in the protocol layer.&lt;/strong&gt; Roy Fielding made caching a first-class architectural constraint in his 2000 dissertation: "Cache constraints require that the data within a response to a request be implicitly or explicitly labeled as cacheable or non-cacheable" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;3&lt;/a&gt;. Each REST endpoint carries &lt;code&gt;Cache-Control&lt;/code&gt; and &lt;code&gt;ETag&lt;/code&gt; headers. CDNs, reverse proxies, and browser caches understand HTTP GET natively. The protocol owns this responsibility.&lt;/p&gt;

&lt;p&gt;That is REST at its idealised best. In practice, the picture is less clean. Research reported by Nordic APIs found that 75% of APIs do not conform to their own specifications &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;4&lt;/a&gt;. Cache headers are misconfigured or omitted. Cache lifetimes are guessed at. The idealised story - where infrastructure handles caching automatically - degrades into partial coverage that teams must supplement with application-level caching. When the ideal breaks, caching responsibility shifts from the protocol layer to the application team, partially or entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why are contract management and governance fragmented in REST?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Contract management lives with the API team, separately from the code.&lt;/strong&gt; OpenAPI specifications must be written, maintained, and kept in sync with actual server behaviour. When the contract is a separate artifact from the implementation, drift is the default state, not the exception. Keeping them aligned requires governance, tooling, and discipline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Governance is implicit and fragmented.&lt;/strong&gt; Each API team sets its own versioning policy, naming conventions, and change processes. There is no central authority deciding how the system evolves - each team governs its own endpoints independently. When aggregation spans teams, governance defaults to whoever builds the aggregation layer.&lt;/p&gt;

&lt;p&gt;Gregor Hohpe captures the underlying pattern: "Flexibility brings complexity; decoupling increases latency; distributing components introduces communication overhead" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;7&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In REST, responsibility for data fetching, schema accuracy, and governance sits with the teams building and consuming the API. Responsibility for security and caching sits close to the infrastructure. The problems are all present. They live on the client side and with the API team.&lt;/p&gt;

&lt;p&gt;Now the responsibility shifts.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How does GraphQL solve over-fetching and shift complexity to the server?
&lt;/h3&gt;

&lt;p&gt;GraphQL moves the centre of gravity from the client to the server by consolidating the API behind a single typed schema. A client describes the data it needs in a single query. The mobile team requests eight fields. The web team requests forty-two. One round trip. No over-fetching. As Lee Byron - co-creator of GraphQL and Executive Director of the GraphQL Foundation - put it: "GraphQL is a trade in the opposite direction - it optimizes for the network" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;8&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data fetching moves to the server.&lt;/strong&gt; The client's aggregation problem from REST dissolves - but the work does not. The GraphQL server resolves each field independently, calling each backend service per resolver. The N+1 problem migrates: in REST, it manifested as N+1 HTTP round trips on the client. In GraphQL, it manifests as N+1 resolver calls on the server, typically addressed using batching patterns like Facebook's DataLoader &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;9&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;10&lt;/a&gt;. Responsibility for efficient data fetching has moved from the client to the server team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do caching and security move into the application layer in GraphQL?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Caching moves from the protocol layer to the application layer.&lt;/strong&gt; GraphQL is protocol agnostic - it can run over HTTP via the &lt;a href="https://graphql.github.io/graphql-over-http/" rel="noopener noreferrer"&gt;GraphQL over HTTP specification&lt;/a&gt; , over WebSockets for subscriptions, or over other transports entirely, much like MCP supports both STDIO and HTTP streaming. In practice, most deployments use HTTP POST, which CDNs and reverse proxies cannot cache natively. The network caching infrastructure that REST can leverage becomes unavailable. Caching still happens - normalised client caches, resolver-level caches, persisted queries over GET - but responsibility moves from infrastructure conventions to application developers who build and maintain these layers explicitly &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;11&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;12&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;13&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most comparisons describe REST caching as automatic and GraphQL caching as absent. In practice, many REST APIs do not properly implement cache headers, and many GraphQL deployments achieve effective caching through persisted queries and normalised client stores. Adobe Experience Manager, for example, serves persisted GraphQL queries over GET - making them cacheable by CDNs and the browser's standard HTTP cache, just like REST endpoints. The operational reality on both sides is more nuanced than the idealised framing suggests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security moves from the infrastructure layer to the application layer.&lt;/strong&gt; REST's per-endpoint rate limiting, WAF pattern matching, and route-level middleware depend on knowing what a request will do based on its URL. GraphQL's single endpoint accepts arbitrary query shapes, which means query depth attacks, batching attacks, and cost analysis all require custom middleware rather than infrastructure configuration &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;14&lt;/a&gt;. Escape's 2024 report found 69% of GraphQL services susceptible to DoS due to lack of resource controls &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;15&lt;/a&gt;. Neither REST nor GraphQL is inherently more or less secure. Security responsibility lives at a different layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why can GraphQL governance become a bottleneck?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Contract management moves from a separate artifact to the implementation itself.&lt;/strong&gt; The GraphQL specification requires introspection - the schema cannot drift from the implementation because it is the implementation &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;16&lt;/a&gt;. GitHub cited this as a primary motivation for adopting GraphQL: "Type safety, introspection, generated documentation, and predictable responses benefit both the maintainers and consumers" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;17&lt;/a&gt;. For complex APIs with many consumers, this eliminates an entire category of integration failures. For simple APIs, it introduces more ceremony than REST's implicit contracts.&lt;/p&gt;

&lt;p&gt;Governance concentrates on the schema owner. A single team owns the schema - and with it, the rules for how the API evolves. This works until multiple domain teams need to evolve it simultaneously, and the schema owner becomes the bottleneck. As Kin Lane observed, GraphQL "makes some very complex things simpler" but "also makes some very simple things more complex" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;18&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No concern disappeared. Each one changed address. Data fetching moved from client to server. Security moved from infrastructure to code. Caching moved from protocol to application. Contract management moved from a separate artifact to the implementation itself. Governance concentrated on a single schema owner.&lt;/p&gt;

&lt;p&gt;The problems that lived with the client now live with the server team. But one problem - governance - is about to move again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Federated GraphQL
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Why does GraphQL federation introduce new complexity?
&lt;/h4&gt;

&lt;p&gt;As the schema grows and more teams need to contribute, the single schema owner becomes a bottleneck. Federation restructures governance by distributing schema ownership. Each team owns a subgraph defining their domain types. A gateway/router composes these into a unified schema at build time and routes queries at runtime.&lt;/p&gt;

&lt;p&gt;Netflix operates at this scale: 300+ subgraphs, 3,000+ types, powering 50-60 applications with a small gateway/router team. Stephen Spalding explains: federation "removes the business logic from the core aggregator, so that it becomes an appliance, like a reverse proxy. That is something you can scale" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;19&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  How are data fetching, caching, and security distributed in federation?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Data fetching lives with each subgraph team for their domain, and with the gateway for &lt;a href="https://wundergraph.com/blog/microservice-dependencies-graphql-federation-requires-provides" rel="noopener noreferrer"&gt;cross-subgraph orchestration&lt;/a&gt;.&lt;/strong&gt; The gateway decomposes a client query into subgraph fetches, executes them in parallel where possible, and merges results. The orchestration responsibility that lived with the client in REST, and with the monolithic server in GraphQL, now lives with the gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching distributes across subgraph boundaries.&lt;/strong&gt; Each subgraph caches its own resolver responses. The gateway can cache composed responses. Client-side normalised caches must understand entity references that span subgraphs. Cache invalidation becomes a distributed problem.&lt;/p&gt;

&lt;p&gt;Entity caching changes this dynamic. When the gateway/router caches resolved entities - a product, a user, a price - by their entity key, caching becomes a platform concern rather than a team-by-team responsibility. Individual subgraph teams no longer need to build and maintain their own caching layers. The platform offers caching as a service, and every team benefits without additional work. This shifts caching from a distributed problem back toward an infrastructure problem - though one that lives at the gateway layer rather than the protocol layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security distributes across layers.&lt;/strong&gt; Field-level authorisation lives with each subgraph team. Query cost limits, depth restrictions, and authentication live with the gateway. The gateway must trust that dozens of teams enforce policies consistently - or centralise authorisation and accept the governance overhead.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why does federation create a governance bottleneck?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Contract management lives with each subgraph team for their types, and with the composition system for cross-subgraph compatibility.&lt;/strong&gt; The schema remains introspectable and self-documenting. But composition introduces a new failure mode: changes in one subgraph can break composition with another. Contract testing and schema validation become essential infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Governance - and this is the most significant shift - lives with humans.&lt;/strong&gt; Discovering who owns which types requires institutional knowledge. Cross-subgraph schema changes require finding owners, scheduling meetings, negotiating entity keys, and tracking approvals. One enterprise with 60+ subgraphs and 25 teams found that a feature spanning three subgraphs took nine weeks from request to production - two days of engineering per team, seven weeks of governance overhead &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;20&lt;/a&gt;. Platform teams in large federations report spending sixty percent of their time as &lt;a href="https://wundergraph.com/blog/platform-engineering-team-bottleneck" rel="noopener noreferrer"&gt;human routers rather than engineers.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The journey continues. Data fetching moved from the client to the server, then to the gateway. Caching distributed across subgraph boundaries. Security distributed across teams. Contract management distributed to subgraph owners with a composition system keeping them aligned. Each of these concerns found a new home.&lt;/p&gt;

&lt;p&gt;But governance - the concern that was implicit and fragmented in REST, then concentrated on the schema owner in GraphQL - has now landed on humans. Platform teams, Slack threads, meeting invitations, approval chains. And human governance does not scale linearly.&lt;/p&gt;

&lt;p&gt;The responsibility is about to move once more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fission
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How does Fission change API design from bottom-up to top-down?
&lt;/h3&gt;

&lt;p&gt;An emerging pattern addresses &lt;a href="https://wundergraph.com/blog/missing-layer-in-graphql-federation" rel="noopener noreferrer"&gt;federation's governance bottleneck&lt;/a&gt; by inverting the direction of schema design. WunderGraph calls the algorithm &lt;a href="https://wundergraph.com/events/virtual/introducing-fission" rel="noopener noreferrer"&gt;Fission&lt;/a&gt; - a name borrowed from nuclear physics, where a heavy nucleus splits into smaller parts while releasing energy. The analogy is deliberate: a single consumer-facing schema splits into subgraph responsibilities, and the energy released is engineering velocity previously lost to human governance.&lt;/p&gt;

&lt;p&gt;In federation, each team builds a subgraph based on what their service can expose. The supergraph is assembled from below - a side effect of composition, not an intentional design. Field names reflect service internals. Type boundaries match team boundaries. The consumer API is whatever falls out.&lt;/p&gt;

&lt;p&gt;Fission starts from the other end. Design the consumer-facing API first - the ideal query a client would write with no technical constraints. Then decompose downward into subgraph responsibilities. The supergraph is the source of truth. Federation's runtime model stays intact; the design process inverts.&lt;/p&gt;

&lt;p&gt;This is a return to GraphQL's origins. When Byron and colleagues built GraphQL at Facebook, the premise was consumer-first: the client describes what it needs, the system figures out fulfilment. Federation drifted producer-first over time. Fission recovers the original philosophy at enterprise scale - &lt;a href="https://wundergraph.com/blog/design-like-a-monolith-implement-as-microservices" rel="noopener noreferrer"&gt;design like a monolith, implement as microservices.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How does Fission reduce governance overhead?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Governance moves from humans to design-time automation.&lt;/strong&gt; Fission identifies who owns which types, analyses the impact of proposed changes, propagates entity keys and federation directives, and routes proposals to affected teams. Composition validation happens at design time, before code is written. The platform team stops being human routers.&lt;/p&gt;

&lt;p&gt;An eBay architect described the underlying problem: "Ownership, checks, and cross-team negotiations have become one of our biggest velocity bottlenecks" &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;21&lt;/a&gt;. Early adopters report that the barrier to proposing schema changes drops significantly when governance shifts from meetings to tooling &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;20&lt;/a&gt;. The pattern is still emerging - these are early signals, not established benchmarks.&lt;/p&gt;

&lt;h3&gt;
  
  
  What new complexity does Fission introduce?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Contract management lives in a dual-view system -&lt;/strong&gt; the supergraph (consumer-facing API) and the subgraphs (what each team implements), kept synchronised by the Fission engine. When a consumer need changes, the tooling derives the subgraph consequences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching, data fetching, and security&lt;/strong&gt; remain where federation placed them. Fission does not alter the runtime architecture. It addresses the design-time governance layer.&lt;/p&gt;

&lt;p&gt;But the conservation law holds. New complexity appears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design-time tooling&lt;/strong&gt; must be maintained, monitored, and trusted. Keeping supergraph and subgraph views synchronised as the graph evolves is a non-trivial engineering challenge. The tooling is young and the pattern is still emerging.&lt;/p&gt;

&lt;p&gt;Consumer-first design requires someone to do the design. When APIs are shaped from consumer needs rather than backend structure, someone must understand those needs deeply. This is API product management - a discipline most engineering organisations have not formalised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Governance&lt;/strong&gt; must be defined, not just enforced. Naming conventions, design standards, composition policies - automation handles enforcement, but humans decide what to enforce. Automated governance with poorly chosen rules produces consistently bad schemas efficiently.&lt;/p&gt;

&lt;p&gt;Responsibility shifted from human governance to design-time tooling, product thinking, and governance definition. Whether this is a favourable trade depends on which of those your organisation is better equipped to handle.&lt;/p&gt;

&lt;p&gt;The journey that began with the client - assembling data from five REST endpoints - has passed through the server, the gateway, the organisation, and arrived at design-time automation. At every stage, the same five concerns were present. At every stage, they lived somewhere different.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Responsibility Map
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do REST, GraphQL, federation, and Fission distribute responsibility?
&lt;/h3&gt;

&lt;p&gt;Across four architectures, the same five concerns appeared. None disappeared. Each one moved.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://docs.google.com/spreadsheets/d/12BKVzjWiMp3nC_sX5Y7hucvkbcf7JUCoE7id9N4njF4/edit?usp=sharing" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocs%2FAHkbwyI7YzLhsVZxovRllPdC6AReaXQARN8x7UW0E4wAaHWGQwI61o3WWoiQwEeo6LmSjhzNin807rPdzxGNOcg0tMLbc3eM5SrNtffDzO-8rtuHNkPrHNHc%3Dw1200-h630-p" height="630" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://docs.google.com/spreadsheets/d/12BKVzjWiMp3nC_sX5Y7hucvkbcf7JUCoE7id9N4njF4/edit?usp=sharing" rel="noopener noreferrer" class="c-link"&gt;
            Responsibility Map - Google Sheets
          &lt;/a&gt;
        &lt;/h2&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fssl.gstatic.com%2Fdocs%2Fspreadsheets%2Fspreadsheets_2023q4.ico" width="256" height="256"&gt;
          docs.google.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Why doesn't any API architecture eliminate complexity?
&lt;/h3&gt;

&lt;p&gt;If you were to draw each architecture as a shape on a radar chart - one axis per concern, measuring not capability but where responsibility sits - you would see four distinct shapes. REST extends outward on data fetching and contract management, pulls inward on security and caching. GraphQL inverts this. Federation distributes runtime concerns but extends human governance. Fission compresses human governance but extends tooling complexity.&lt;/p&gt;

&lt;p&gt;The total area enclosed by each shape is roughly equal. No architecture is smaller. Each one distributes the work differently - across different layers, different teams, different disciplines.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Which API architecture is the best choice?
&lt;/h3&gt;

&lt;p&gt;The Postman 2025 State of the API report shows REST at 93% adoption and GraphQL at 33% &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;22&lt;/a&gt;. Those numbers describe coexistence, not competition. Organisations adopt different responsibility distributions for different contexts - often running both simultaneously.&lt;/p&gt;

&lt;p&gt;The question was never which technology is better. It was always: given your team's strengths, your consumers' needs, your organisational structure, and your operational maturity - where should each responsibility live?&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the real question behind REST vs GraphQL?
&lt;/h3&gt;

&lt;p&gt;A reader who finishes this article thinking about &lt;a href="https://wundergraph.com/blog/fact-checking-graphql-vs-rest" rel="noopener noreferrer"&gt;REST versus GraphQL&lt;/a&gt; has missed the point. The point is: who in your system or organisation is responsible for each problem - and is that where you want the responsibility to be?&lt;/p&gt;

&lt;p&gt;Architecture is not about eliminating complexity. It is about deciding where it lives, and who owns it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How WunderGraph Helps
&lt;/h2&gt;

&lt;p&gt;If you're running federated GraphQL and recognise the governance bottleneck described in this article, WunderGraph builds the platform to address it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://cosmo.wundergraph.com/" rel="noopener noreferrer"&gt;WunderGraph Cosmo&lt;/a&gt;  is the federation platform - the control plane for composing, managing, and operating your federated graph. It handles schema composition, analytics, and the operational concerns that come with running federation at scale.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://hub.wundergraph.com/" rel="noopener noreferrer"&gt;WunderGraph Hub&lt;/a&gt;  is the design-time governance layer - schema proposals, impact analysis, automated composition validation, and routing changes to the right teams. Hub is where Fission lives, inverting the design process so you start from the use-case and design with intent.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The concerns described in this article don't disappear. WunderGraph moves them to where tooling can manage them - so your engineers can focus on building, not coordinating.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] Tesler, L. "Law of Conservation of Complexity." (~1984). &lt;a href="https://en.wikipedia.org/wiki/Law_of_conservation_of_complexity" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Law_of_conservation_of_complexity&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] Brooks, F.P. "No Silver Bullet - Essence and Accident in Software Engineering." (1986). &lt;a href="https://worrydream.com/refs/Brooks_1986_-_No_Silver_Bullet.pdf" rel="noopener noreferrer"&gt;https://worrydream.com/refs/Brooks_1986_-_No_Silver_Bullet.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] Fielding, R.T. "Architectural Styles and the Design of Network-based Software Architectures." PhD dissertation, UC Irvine, Chapter 5. (2000). &lt;a href="https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm" rel="noopener noreferrer"&gt;https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[4] Nordic APIs. "Understanding the Root Causes of API Drift." &lt;a href="https://nordicapis.com/understanding-the-root-causes-of-api-drift/" rel="noopener noreferrer"&gt;https://nordicapis.com/understanding-the-root-causes-of-api-drift/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[5] Calcado, P. "Some Thoughts on GraphQL vs. BFF." (2019). &lt;a href="https://philcalcado.com/2019/07/12/some_thoughts_graphql_bff.html" rel="noopener noreferrer"&gt;https://philcalcado.com/2019/07/12/some_thoughts_graphql_bff.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[6] Fowler, M. Tweet on BFF and GraphQL. &lt;a href="https://x.com/martinfowler/status/1149688467829354501" rel="noopener noreferrer"&gt;https://x.com/martinfowler/status/1149688467829354501&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[7] Hohpe, G. "The Software Architect Elevator." O'Reilly. (2020). &lt;a href="https://architectelevator.com/book/" rel="noopener noreferrer"&gt;https://architectelevator.com/book/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[8] Byron, L. "Interview with GraphQL Co-Creator Lee Byron." Nordic APIs. &lt;a href="https://nordicapis.com/interview-with-graphql-co-creator-lee-byron/" rel="noopener noreferrer"&gt;https://nordicapis.com/interview-with-graphql-co-creator-lee-byron/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[9] Shapton, L. et al. "Solving the N+1 Problem for GraphQL through Batching." Shopify Engineering. &lt;a href="https://shopify.engineering/solving-the-n-1-problem-for-graphql-through-batching" rel="noopener noreferrer"&gt;https://shopify.engineering/solving-the-n-1-problem-for-graphql-through-batching&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[10] Facebook. "GraphQL DataLoader." &lt;a href="https://github.com/graphql/dataloader" rel="noopener noreferrer"&gt;https://github.com/graphql/dataloader&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[11] Sturgeon, P. "GraphQL vs REST: Caching." APIs You Won't Hate. &lt;a href="https://apisyouwonthate.com/blog/graphql-vs-rest-caching/" rel="noopener noreferrer"&gt;https://apisyouwonthate.com/blog/graphql-vs-rest-caching/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[12] Byron, L. Reactiflux Q&amp;amp;A transcript. &lt;a href="https://www.reactiflux.com/transcripts/lee-byron" rel="noopener noreferrer"&gt;https://www.reactiflux.com/transcripts/lee-byron&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[13] Giroux, M-A. "GraphQL &amp;amp; Caching: The Elephant in the Room." Apollo GraphQL Blog. &lt;a href="https://apollographql.com/blog/backend/caching/graphql-caching-the-elephant-in-the-room" rel="noopener noreferrer"&gt;https://apollographql.com/blog/backend/caching/graphql-caching-the-elephant-in-the-room&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[14] OWASP. "GraphQL Cheat Sheet." &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html" rel="noopener noreferrer"&gt;https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html&lt;/a&gt; — See also: "REST Security Cheat Sheet." &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[15] Escape. "The State of GraphQL Security 2024." &lt;a href="https://escape.tech/resources/the-state-of-graphql-security-2024" rel="noopener noreferrer"&gt;https://escape.tech/resources/the-state-of-graphql-security-2024&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[16] GraphQL Foundation. "GraphQL Specification, Section 4 - Introspection." &lt;a href="https://spec.graphql.org/October2021/" rel="noopener noreferrer"&gt;https://spec.graphql.org/October2021/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[17] GitHub Engineering. "The GitHub GraphQL API." (2016). &lt;a href="https://github.blog/developer-skills/github/the-github-graphql-api/" rel="noopener noreferrer"&gt;https://github.blog/developer-skills/github/the-github-graphql-api/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[18] Lane, K. "My GraphQL Thoughts After Almost Two Years." API Evangelist. (2018). &lt;a href="https://apievangelist.com/2018/04/16/graphql-thoughts-after-almost-two-years/" rel="noopener noreferrer"&gt;https://apievangelist.com/2018/04/16/graphql-thoughts-after-almost-two-years/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[19] Shin, J. &amp;amp; Spalding, S. "How Netflix Scales Its API with GraphQL Federation." InfoQ. &lt;a href="https://www.infoq.com/presentations/netflix-api-graphql-federation/" rel="noopener noreferrer"&gt;https://www.infoq.com/presentations/netflix-api-graphql-federation/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[20] WunderGraph. Customer case study: 60+ subgraphs, 25 teams. 75-87% schema change cycle time reduction, 4x monthly schema proposals after adopting Fission. (2026).&lt;/p&gt;

&lt;p&gt;[21] WunderGraph. eBay architect testimonial: "Ownership, checks, and cross-team negotiations have become one of our biggest velocity bottlenecks." (2026).&lt;/p&gt;

&lt;p&gt;[22] Postman. "2025 State of the API Report." &lt;a href="https://www.postman.com/state-of-api/2025/" rel="noopener noreferrer"&gt;https://www.postman.com/state-of-api/2025/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[23] Microsoft. "Chatty I/O Antipattern." Azure Architecture Centre. &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[24] Hamer, N. "The Conservation of Complexity in Software Architecture." Capgemini Engineering. &lt;a href="https://capgemini.github.io/architecture/The-Conservation-of-Complexity-in-Software-Architecture/" rel="noopener noreferrer"&gt;https://capgemini.github.io/architecture/The-Conservation-of-Complexity-in-Software-Architecture/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[25] Fielding, R.T. "REST APIs must be hypertext-driven." (2008). &lt;a href="https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven" rel="noopener noreferrer"&gt;https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This article was originally published on: &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references" rel="noopener noreferrer"&gt;https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity#references&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>restapi</category>
      <category>api</category>
      <category>microservices</category>
    </item>
    <item>
      <title>GraphQL vs REST: 18 Claims Fact-Checked with Primary Sources (2026)</title>
      <dc:creator>WunderGraph</dc:creator>
      <pubDate>Sat, 18 Apr 2026 01:23:38 +0000</pubDate>
      <link>https://forem.com/wundergraph/graphql-vs-rest-18-claims-fact-checked-with-primary-sources-2026-117l</link>
      <guid>https://forem.com/wundergraph/graphql-vs-rest-18-claims-fact-checked-with-primary-sources-2026-117l</guid>
      <description>&lt;p&gt;What happens if we take a scientific approach to analyzing the most common claims about GraphQL vs REST? You might be thinking that you know the answer to most of these claims, and even I thought I did before I started this research.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SPOILER ALERT: GraphQL is inferior to REST because it breaks HTTP caching is actually misleading. The infamous N+1 problem? It's real, but REST has it too, say multiple high-profile sources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of these claims are repeated so often that they become accepted wisdom, and people stop questioning them. I always knew that some of them were wrong, but what I didn't expect was how even the most widely repeated claims often turned out to be misleading or incomplete when I traced them back to their original sources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://wundergraph.com/" rel="noopener noreferrer"&gt;WunderGraph&lt;/a&gt; . We build GraphQL Federation infrastructure. I have a horse in this race, and you should know that upfront.&lt;/p&gt;

&lt;p&gt;That said, my personal opinion is that both GraphQL and REST are valuable tools for different use cases. I would always recommend our customers to use both solutions side by side if they can complement each other, but ultimately, it's about finding the right technical solution to solve a business problem, not about picking a side in a technology debate.&lt;/p&gt;

&lt;p&gt;I traced these commonly repeated assertions to their original sources where a verifiable primary source was available, and used secondary or vendor sources where primary evidence was not accessible.&lt;/p&gt;

&lt;p&gt;Where only vendor benchmarks or secondary sources are available, those are labeled explicitly and treated as directional rather than conclusive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://docs.google.com/spreadsheets/d/1gw2lRlbwmCf0x4blEA1ej_juMTv4ASNKWQwUVLd7m-A/edit?usp=sharing" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocs%2FAHkbwyLKHNBx6EfsfONw6Xitzt4RcX9leNtGai6SVjTwLzroHFZnTxYpN52n2C76rvhfNAMSzAcmmAO4fOiQ4kQMPIVNbIU-j9Pt7Es2NV8ESx1okONq_Vn1%3Dw1200-h630-p" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://docs.google.com/spreadsheets/d/1gw2lRlbwmCf0x4blEA1ej_juMTv4ASNKWQwUVLd7m-A/edit?usp=sharing" rel="noopener noreferrer" class="c-link"&gt;
            Summary - GraphQL vs REST: 18 Claims Fact-Checked with Primary Sources (2026) - Google Sheets
          &lt;/a&gt;
        &lt;/h2&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fssl.gstatic.com%2Fdocs%2Fspreadsheets%2Fspreadsheets_2023q4.ico"&gt;
          docs.google.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Here is what the evidence suggests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part I: What the Evidence Says
&lt;/h2&gt;

&lt;p&gt;This section focuses on verifiable claims and their sources.&lt;/p&gt;

&lt;p&gt;Every claim is sourced where a verifiable reference is available. A fair comparison requires evaluating both technologies under equivalent assumptions: default vs. default and optimized vs. optimized.&lt;/p&gt;

&lt;p&gt;In this analysis, “default” means out‑of‑the‑box behavior, while “production” means commonly adopted patterns in mature or scaled deployments.&lt;/p&gt;

&lt;p&gt;Where an original claim is right, I say so; where the evidence disagrees, I show why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Origin Story: How Facebook Actually Built GraphQL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Claim 1: "Facebook built GraphQL for hundreds of internal microservices"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Historically imprecise.
&lt;/h4&gt;

&lt;p&gt;The 2012 timeline, the iOS News Feed context, and the mobile bandwidth constraints are all real.&lt;/p&gt;

&lt;p&gt;I could not find a primary source describing Facebook's 2012 backend as a system composed of "hundreds of microservices," or stating that GraphQL was created to unify such a system. The &lt;a href="https://graphql.org/learn/federation/" rel="noopener noreferrer"&gt;GraphQL Foundation's own Federation&lt;/a&gt; page  states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Meta (formerly Facebook), where GraphQL was created, has continued to use a monolithic GraphQL API since 2012.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Facebook ran a massive PHP codebase, scaled through &lt;a href="https://engineering.fb.com/2011/12/09/open-source/the-hiphop-virtual-machine/" rel="noopener noreferrer"&gt;HipHop&lt;/a&gt;  (a PHP-to-C++ transpiler) and later &lt;a href="https://hhvm.com/" rel="noopener noreferrer"&gt;HHVM&lt;/a&gt;  (a just-in-time compiler for PHP). &lt;a href="https://github.com/kmapb" rel="noopener noreferrer"&gt;Keith Adams&lt;/a&gt; , Facebook's Chief Architect and HHVM team lead, describes how Facebook scaled a large, unified PHP application in &lt;a href="https://softwareengineeringdaily.com/2019/07/15/facebook-php-with-keith-adams/" rel="noopener noreferrer"&gt;Software Engineering Daily interviews&lt;/a&gt;  and &lt;a href="https://www.infoq.com/interviews/adams-php-facebook/" rel="noopener noreferrer"&gt;InfoQ presentations&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;The strategy emphasized scaling PHP through compilation rather than prioritizing early service decomposition. On top of the PHP application sat TAO , a graph-structured data layer built on MySQL and memcached. &lt;a href="https://engineering.fb.com/2013/06/25/core-infra/tao-the-power-of-the-graph/" rel="noopener noreferrer"&gt;TAO&lt;/a&gt; provided a unified way to access social graph data. The backend included multiple systems and data sources, but primary sources do not describe it as a microservice-oriented architecture.&lt;/p&gt;

&lt;p&gt;Meta's &lt;a href="https://engineering.fb.com/2015/09/14/core-infra/graphql-a-data-query-language/" rel="noopener noreferrer"&gt;engineering blog&lt;/a&gt;  describes "existing databases and business logic," rather than framing the problem as a microservice-oriented API layer. The common argument is that GraphQL was built to manage hundreds of microservices. There is no primary source clearly supporting that framing of the original problem GraphQL was designed to solve, and its later use in microservice architectures does not change that original motivation.&lt;/p&gt;

&lt;p&gt;In short, microservices later became a use case for GraphQL, not the original design driver; saying GraphQL was “built for hundreds of microservices” reverses that causal order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 2: "Lee Byron, Dan Schafer, and Nick Schrock built GraphQL"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Generally accurate, but slightly simplified.
&lt;/h4&gt;

&lt;p&gt;Primary sources and historical accounts consistently credit &lt;a href="https://github.com/leebyron" rel="noopener noreferrer"&gt;Lee Byron&lt;/a&gt; , &lt;a href="https://github.com/dschafer" rel="noopener noreferrer"&gt;Dan Schafer&lt;/a&gt; , and Nick Schrock  as the key creators of GraphQL. &lt;a href="https://github.com/schrockn" rel="noopener noreferrer"&gt;Nick Schrock&lt;/a&gt; is widely described as writing the initial prototype, reportedly called "SuperGraph." Together with Dan Schafer and Lee Byron, they developed GraphQL and brought it into production at Facebook around 2012.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nordicapis.com/interview-with-graphql-co-creator-lee-byron/" rel="noopener noreferrer"&gt;Nordic APIs interviewed Lee Byron&lt;/a&gt;  about the creation process, and &lt;a href="https://hpcwire.com/bigdatawire/2019/06/27/graphql-creators-provide-a-tutorial-on-teamwork/" rel="noopener noreferrer"&gt;Datanami&lt;/a&gt;  covers the contributions of all three.&lt;/p&gt;

&lt;p&gt;However, this framing simplifies the broader effort. GraphQL was developed within a larger team at Facebook, and multiple engineers contributed to its design, implementation, and adoption. The claim is accurate at a high level, but should be understood as identifying the primary contributors rather than the full set of people involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claim 3: "Facebook open-sourced GraphQL in 2015"
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Verdict: Accurate.
&lt;/h4&gt;

&lt;p&gt;Facebook publicly released GraphQL on &lt;a href="https://engineering.fb.com/2015/09/14/core-infra/graphql-a-data-query-language/" rel="noopener noreferrer"&gt;September 14, 2015&lt;/a&gt; , following its public introduction at the React Europe conference in Paris earlier that year. In 2018, GraphQL was &lt;a href="https://techcrunch.com/2018/11/06/facebooks-graphql-gets-its-own-open-source-foundation/" rel="noopener noreferrer"&gt;moved to the GraphQL Foundation&lt;/a&gt; , where it is now governed under the &lt;a href="https://www.linuxfoundation.org/" rel="noopener noreferrer"&gt;Linux Foundation&lt;/a&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  The GraphQL N+1 Problem: Does REST Have It Too?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Claim 4: "Fetching 25 users with posts in GraphQL results in 26 database queries"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Partially true (implementation-dependent, not inherent to GraphQL).
&lt;/h4&gt;

&lt;p&gt;The arithmetic is correct for one specific scenario, and the official &lt;a href="https://www.graphql-js.org/docs/n1-dataloader/" rel="noopener noreferrer"&gt;GraphQL.js documentation&lt;/a&gt;  confirms it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In GraphQL.js, each field resolver runs independently. There's no built-in coordination between resolvers, and no automatic batching.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This behavior is not defined by the GraphQL specification, but by how resolver-based servers like GraphQL.js execute queries. When you query for 25 users and their posts, the server may first execute one query to fetch all 25 users. Then, for each user, it may execute a separate query to fetch that user's posts. That's 25 + 1 = 26 database queries, which is the classic "N+1 problem": N queries for related data, plus 1 for the initial list.&lt;/p&gt;

&lt;p&gt;For servers built on GraphQL.js without DataLoader, this is a common outcome in resolver-based implementations. In practice, many GraphQL deployments introduce batching layers, such as DataLoader, or use engines that compile queries into optimized database queries to avoid this pattern. But the picture is incomplete in two ways.&lt;/p&gt;

&lt;p&gt;First, some GraphQL engines addressed this years ago. Tools like &lt;a href="https://hasura.io/blog/architecture-of-a-high-performance-graphql-to-sql-server-58d9944b8a87" rel="noopener noreferrer"&gt;Hasura&lt;/a&gt;  and &lt;a href="https://postgraphile.org/" rel="noopener noreferrer"&gt;PostGraphile&lt;/a&gt;  don’t rely on per-field resolvers for database-backed queries. They compile GraphQL queries directly into optimized SQL with JOINs. In these systems, the 25-users-with-posts query can often be executed as a single database query. In its own benchmarks, Hasura reports up to &lt;a href="https://hasura.io/blog/compiling-graphql-for-optimal-performance-going-beyond-dataloader" rel="noopener noreferrer"&gt;higher throughput&lt;/a&gt;  than hand-written Node.js code using DataLoader. This is based on vendor benchmarks and should be treated as directional rather than conclusive.&lt;/p&gt;

&lt;p&gt;Second, REST can exhibit a similar N+1 pattern at the network layer, as shown in the next claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claim 5: "In REST, fetching 25 users with their posts is two calls"
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Verdict: Misleading.
&lt;/h4&gt;

&lt;p&gt;A common claim is that REST handles this in two calls:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /users&lt;/code&gt; and &lt;code&gt;GET /users/:id/posts.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But look at that second URL. The &lt;code&gt;:id&lt;/code&gt; is a path parameter.&lt;/p&gt;

&lt;p&gt;Without a dedicated batch endpoint, the REST client has to call &lt;code&gt;GET /users/1/posts&lt;/code&gt;, &lt;code&gt;GET /users/2/posts&lt;/code&gt;, ... &lt;code&gt;GET /users/25/posts&lt;/code&gt;. Without a batch or embedding mechanism, this can result in 26 HTTP round-trips. In many systems, 26 HTTP round-trips (each requiring a full network request-response cycle) is going to be more expensive than 26 database queries behind a single HTTP call.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://restfulapi.net/rest-api-n-1-problem/" rel="noopener noreferrer"&gt;REST API Tutorial&lt;/a&gt;  documents this exact problem. Microsoft's Azure Architecture Center calls this the &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/" rel="noopener noreferrer"&gt;Chatty I/O antipattern&lt;/a&gt;  and explicitly names it "the N+1 problem." N+1 is not a GraphQL-specific problem. It manifests at different layers.&lt;/p&gt;

&lt;p&gt;To get it down to two calls, REST needs a purpose-built batch endpoint like &lt;code&gt;GET /users/posts?ids=1,2,...,25&lt;/code&gt;. That endpoint either uses POST (typically uncacheable in practice) or GET with dynamic query parameters where every unique combination of user IDs produces a different URL.&lt;/p&gt;

&lt;p&gt;And it's not just the combination that matters; it's also the order, unless it is normalized.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;?ids=1,2,3&lt;/code&gt; and &lt;code&gt;?ids=3,1,2&lt;/code&gt; are different URLs to a CDN, which means different permutations of the same IDs can produce different cache keys. In practice, many REST architectures introduce aggregation layers or backend-for-frontend services to avoid this pattern. We'll come back to why this matters in the caching section.&lt;/p&gt;

&lt;p&gt;The fair comparison is either naive-to-naive (26 HTTP calls vs. 26 DB queries) or optimized-to-optimized (batch endpoint vs. DataLoader or query compilation, both producing 1-2 queries). The original claim oversimplifies the design trade‑offs by mixing optimized REST with naive GraphQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 6: "DataLoader is not built into GraphQL and is not part of the specification"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: True (but often misunderstood in practice).
&lt;/h4&gt;

&lt;p&gt;The statement is technically correct at the specification level, but incomplete in how GraphQL is used in practice.&lt;/p&gt;

&lt;p&gt;DataLoader was originally developed at Facebook, and its copyrights were later transferred to the GraphQL Foundation. It is now maintained as part of the broader GraphQL ecosystem. It batches and deduplicates data fetches within a single request, does not share results across requests, and is not a replacement for application-level caching.&lt;/p&gt;

&lt;p&gt;When multiple resolvers request the same data (e.g., "fetch user 1", "fetch user 5", "fetch user 12"), DataLoader collects them into a single batch call and issues one combined fetch. Instead of 25 individual database queries, a single batched query can often fetch all 25 users at once.&lt;/p&gt;

&lt;p&gt;DataLoader is generic and not specific to GraphQL, the same pattern can be used in REST APIs, microservices, or any system with N+1-style access patterns.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://netflix.github.io/dgs/data-loaders/" rel="noopener noreferrer"&gt;Netflix DGS&lt;/a&gt;  provides &lt;code&gt;@DgsDataLoader&lt;/code&gt; annotations in Java, &lt;a href="https://gqlgen.com/reference/dataloaders/" rel="noopener noreferrer"&gt;gqlgen&lt;/a&gt;  documents DataLoader integration for Go, and &lt;a href="https://graphql-ruby.org/" rel="noopener noreferrer"&gt;GraphQL-Ruby&lt;/a&gt;  has &lt;code&gt;GraphQL::Dataloader&lt;/code&gt; built in. Query compilation engines (Hasura, PostGraphile) bypass the need for DataLoader by generating optimized SQL directly.&lt;/p&gt;

&lt;p&gt;DataLoader reduces redundant fetches, but it introduces additional complexity in resolver design and requires careful scoping and key management.&lt;/p&gt;

&lt;p&gt;REST does not define a standard batching mechanism at the protocol level. When batching is needed, it is handled through API design (such as bulk endpoints), infrastructure, or framework-specific solutions. Some specifications attempt to address this, such as &lt;a href="https://www.odata.org/" rel="noopener noreferrer"&gt;OData’s&lt;/a&gt;  batch format or &lt;a href="https://jsonapi.org/" rel="noopener noreferrer"&gt;JSON:API’s&lt;/a&gt;  compound documents, but adoption is inconsistent.&lt;/p&gt;

&lt;p&gt;These differences reflect where batching is handled. GraphQL solutions often address it at the execution layer, while REST implementations handle it through API design or infrastructure. DataLoader is therefore just one of several batching patterns in the GraphQL ecosystem, not the only way to avoid N+1.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL vs REST Performance: What Benchmarks Actually Show
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Claim 7: "REST delivers nearly half the latency and 70% more requests per second"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Context-dependent and often misapplied.
&lt;/h4&gt;

&lt;p&gt;These numbers come from a &lt;a href="https://medium.com/jds-engineering/i-was-curious-which-is-faster-rest-or-graphql-so-i-tested-themto-say-hello-world-d6c28fec4f17" rel="noopener noreferrer"&gt;Medium benchmark&lt;/a&gt;  that tested a Hello World‑style endpoint and is representative of a broader class of “Hello World” REST vs GraphQL benchmarks, not necessarily the exact one cited by any particular critic:&lt;/p&gt;

&lt;p&gt;REST at ~7.68ms latency vs. GraphQL at ~13.51ms, and ~11,972 RPS vs. ~7,085 RPS.&lt;/p&gt;

&lt;p&gt;That kind of workload mostly measures protocol overhead on a flat response, not the multi-resource retrieval problem GraphQL was designed to address. It is also informal evidence from a single implementation, not a peer-reviewed study. For relational or multi-resource data retrieval, several studies report the opposite result under some workloads.&lt;/p&gt;

&lt;p&gt;A peer-reviewed study by Seabra, Nazário, and Pinto, published at &lt;a href="https://dl.acm.org/doi/10.1145/3357141.3357149" rel="noopener noreferrer"&gt;SBCARS '19&lt;/a&gt;  (proceedings in the ACM Digital Library), found that migrating from REST to GraphQL reduced latency in two of three tested applications, often by reducing the number of network round-trips. Under workloads above 3,000 requests, REST outperformed GraphQL.&lt;/p&gt;

&lt;p&gt;A study by Jin, Cordingly, Zhao, and Lloyd at &lt;a href="https://faculty.washington.edu/wlloyd/papers/graphql-camera-ready.pdf" rel="noopener noreferrer"&gt;UW Tacoma&lt;/a&gt; , presented at WoSC10 (December 2024), evaluated a 10.9‑million‑row CMS dataset on AWS, and reported &lt;strong&gt;25–67%&lt;/strong&gt; lower average latency for GraphQL on several data-intensive operations, while REST retained an advantage at the highest concurrency levels.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://doaj.org/article/d3438a3cda37412aa9a755c120d2a763" rel="noopener noreferrer"&gt;separate study&lt;/a&gt;  by Lawi, Panggabean, and Yoshida, found REST was faster on response time and throughput in that test setup, while GraphQL used about &lt;strong&gt;37-40%&lt;/strong&gt; less CPU and memory. That supports a real resource-efficiency tradeoff, though the result is specific to that workload.&lt;/p&gt;

&lt;p&gt;Elghazal, Aneiba, and Shahra presented at &lt;a href="https://www.scitepress.org/Papers/2025/137299/137299.pdf" rel="noopener noreferrer"&gt;WEBIST 2025&lt;/a&gt;  and found REST ~3.7x faster on response time in a Go-based microservices setup, while GraphQL transferred significantly less data overall, reducing total data volume from several gigabytes to a few hundred megabytes. The 3.7x latency gap is larger than what other studies report, which suggests strong sensitivity to implementation and workload.&lt;/p&gt;

&lt;h4&gt;
  
  
  Across these studies, a general pattern emerges
&lt;/h4&gt;

&lt;p&gt;Performance varies by workload, and neither approach is consistently faster. REST often has lower per-request overhead for simple payloads and at high concurrency. GraphQL can have lower total latency when it reduces or eliminates multiple round-trips, transfers less data, or uses fewer server resources.&lt;/p&gt;

&lt;p&gt;The right choice depends on the workload, including schema design, query patterns, and implementation details. Citing a flat CRUD benchmark to evaluate a system designed for relational queries is technically correct, but it measures a different use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Caching vs REST Caching: The Real Tradeoffs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Claim 8: "GraphQL uses POST to a single endpoint; HTTP caching does not work"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Partially true (default vs production setups).
&lt;/h4&gt;

&lt;p&gt;Standard GraphQL implementations use POST to &lt;code&gt;/graphql&lt;/code&gt;. POST requests are not cached by browsers, CDNs, or reverse proxies by default. This is a real limitation and worth understanding.&lt;/p&gt;

&lt;p&gt;HTTP caching relies on two things: the request URL being stable across clients, and the HTTP method being GET in most common caching setups. When every GraphQL request is a POST with a body that is not used as part of the cache key by intermediaries, caches cannot reliably match requests to stored responses.&lt;/p&gt;

&lt;p&gt;More precisely, the issue is a lack of stable cache keys rather than POST itself, since POST responses can be cached when explicitly configured. By default, this gives REST an advantage for simple, per-resource caching, while GraphQL requires additional setup to achieve similar behavior.&lt;/p&gt;

&lt;p&gt;But the comparison with REST is asymmetric.&lt;/p&gt;

&lt;p&gt;As we established in Claim 5, REST serving relational data often either makes multiple individual GET requests (cacheable but N+1 round-trips) or uses a batch endpoint (efficient but less cacheable in practice). In many production systems, REST avoids this trade-off by relying on per-resource caching combined with client-side or edge aggregation (for example, a BFF or gateway), rather than batching at the CDN layer.&lt;/p&gt;

&lt;p&gt;A REST batch endpoint like &lt;code&gt;GET /users/posts?ids=1,2,3,...,25&lt;/code&gt; produces a different URL for every unique combination of requested users. And as we noted in Claim 5, it's not just the combination that matters, but also the order: &lt;code&gt;?ids=1,2,3&lt;/code&gt; and &lt;code&gt;?ids=3,1,2&lt;/code&gt; are different cache keys to a CDN. CDN cache hit rates for these parameterized URLs can be lower in practice, depending on usage patterns.&lt;/p&gt;

&lt;p&gt;Even if you do manage to cache batch URLs, invalidation becomes expensive. When a single entity changes (say, user 7 updates their profile), you need to invalidate every cached batch URL that contains that user's ID. Most CDNs have no built-in way to compute which URLs contain which IDs without additional tagging or indexing. A single entity change can invalidate an unpredictable number of cached responses. This comparison mixes HTTP-level caching with client-side caching, which solve different problems but are often combined in real systems.&lt;/p&gt;

&lt;p&gt;Compare this to GraphQL's normalized client cache (Apollo Client, Relay), where invalidation happens at the entity level. This operates at the client layer and does not replace HTTP-level caching. User 7's data is stored once by its cache ID. When it changes, only that entity updates, and every view referencing it refreshes automatically. No URL permutation problem. No batch invalidation cascade.&lt;/p&gt;

&lt;p&gt;These trade-offs can make it difficult to simultaneously optimize REST for both batch efficiency (for the N+1 comparison) and high HTTP cache reuse (which requires stable, per-resource URLs). These approaches are in tension. Optimizing for batch efficiency reduces round-trips, but tends to lower CDN cache hit rates and complicate invalidation.&lt;/p&gt;

&lt;h4&gt;
  
  
  A common production solution: Persisted Queries.
&lt;/h4&gt;

&lt;p&gt;Also called trusted documents, &lt;a href="https://benjie.dev/graphql/trusted-documents" rel="noopener noreferrer"&gt;persisted queries&lt;/a&gt;  are a recommended production pattern for GraphQL APIs. Instead of sending the full query string in a POST body, the client sends a hash: &lt;code&gt;GET /graphql?extensions={"persistedQuery":{"sha256Hash":"abc123"}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This approach is most effective for first-party APIs, where the set of allowed operations can be controlled.&lt;/p&gt;

&lt;p&gt;It can be implemented as a GET request with a more stable URL when queries are pre-registered. This requires maintaining a registry of allowed operations and coordinating deployments between client and server. CDNs can cache it like other GET requests when configured appropriately.&lt;/p&gt;

&lt;p&gt;However, this approach is less flexible for highly dynamic queries and is harder to apply in public or third-party API scenarios. Variables still affect cache keys, so different inputs produce different cache entries, and fragmentation can still occur depending on usage patterns. &lt;a href="https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/headless/deployment/dispatcher-caching" rel="noopener noreferrer"&gt;Adobe Experience Manager&lt;/a&gt; , for example, recommends persisted queries as the preferred way to enable CDN caching for GraphQL.&lt;/p&gt;

&lt;p&gt;Persisted queries can help address the caching problem (GET requests, stable URLs), the security problem (only pre-registered queries execute), and the client size problem (works with plain fetch()).&lt;/p&gt;

&lt;p&gt;While some systems allow GET with query‑string GraphQL, it is brittle in practice, which is why POST‑by‑default is the effective pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 9: "56% of teams report caching challenges with GraphQL"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Partially true (source unclear, underlying concern valid).
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;56%&lt;/strong&gt; figure is widely cited, attributed variously to an "Apollo 2024 survey" or a "JetBrains Developer Survey." I could not locate a clear primary source for this exact number. Given the lack of a verifiable primary source, this statistic should be treated as anecdotal rather than evidence‑based.&lt;/p&gt;

&lt;p&gt;Even if the number were accurate, it would still need to be interpreted carefully. Regardless of that specific percentage, the underlying concern is real: GraphQL caching is more complex by default, because it often handles multi-resource queries that do not map cleanly to per-resource HTTP caching and pushes more responsibility onto the client and gateway layers. This means teams must make explicit architectural choices to get effective caching, rather than relying on default HTTP behavior.&lt;/p&gt;

&lt;p&gt;REST systems can avoid some of this complexity through per-resource caching, but face their own trade-offs when aggregating data or batching requests, including reduced cache reuse and more complex invalidation. At the same time, the problem is well understood, and there are established patterns to address it: persisted queries for CDN caching (see Claim 8), and normalized client caches for application-level caching (see Claim 10).&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 10: "Apollo Client weighs 43 KB gzipped"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Misleading comparison (incomplete framing).
&lt;/h4&gt;

&lt;p&gt;I often see comparisons framed as Apollo Client (43 KB) versus &lt;code&gt;fetch()&lt;/code&gt; (0 KB), implying GraphQL requires a heavy client library while REST uses native browser APIs.&lt;/p&gt;

&lt;p&gt;Historically, measurements around 40–45 KB gzipped were reasonable for older Apollo Client &lt;a href="https://www.apollographql.com/docs/react/development-testing/reducing-bundle-size" rel="noopener noreferrer"&gt;bundles&lt;/a&gt; , but recent v4.x releases have reduced bundle size and made features more modular. Regardless, that is not a cost of GraphQL itself. The same concern exists for feature‑rich REST clients like &lt;a href="https://swr.vercel.app/" rel="noopener noreferrer"&gt;SWR&lt;/a&gt;  or &lt;a href="https://tanstack.com/query/latest" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt; . It is a misleading comparison because both REST and GraphQL can be implemented with nothing more than fetch(). Therefore, the theoretical baseline for both styles is 0 KB.&lt;/p&gt;

&lt;p&gt;GraphQL is an HTTP request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{ users { name } }&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No library is strictly required.&lt;/p&gt;

&lt;p&gt;With persisted queries, it can be simpler at runtime: a GET request with a hash parameter. This requires pre-registering operations and coordinating between client and server.&lt;/p&gt;

&lt;p&gt;In practice, many GraphQL applications adopt libraries like Apollo Client or Relay because they solve common problems around caching, state management, and request orchestration. The additional bundle size in libraries like Apollo Client pays for these capabilities.&lt;/p&gt;

&lt;p&gt;Similar trade-offs exist in REST clients, though GraphQL clients often include more built-in behavior due to the structure of the query model. In both cases, these libraries shift complexity from the API layer to the client. Without it, you often end up rebuilding similar logic yourself or pulling in REST-side alternatives like TanStack Query or SWR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.apollographql.com/docs/react" rel="noopener noreferrer"&gt;Apollo Client&lt;/a&gt;  provides a normalized cache that stores entities by ID and enables automatic UI updates when cached data changes. When a mutation returns updated data, Apollo can update the cache and reflect those changes in many relevant queries without requiring manual refetching.&lt;/p&gt;

&lt;p&gt;On top of the normalized cache, Apollo provides additional features.&lt;br&gt;
These include optimistic updates, pagination helpers, request deduplication, and partial error rendering.&lt;br&gt;
It also includes local state management and DevTools for inspecting cache data and queries.&lt;/p&gt;

&lt;p&gt;Relay  (by Meta) provides additional compile-time guarantees. Its ahead-of-time compiler runs at build time, catching malformed queries before the app ships. Fragment colocation lets each component declare the data it needs, and data masking ensures components only access the data defined in their fragments, preventing accidental coupling between components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://relay.dev/" rel="noopener noreferrer"&gt;Relay&lt;/a&gt; includes configurable garbage collection for unreferenced records in its normalized store, helping limit memory growth in long-running applications. These features address common data consistency and state management problems.&lt;/p&gt;

&lt;p&gt;In applications where the same entity appears in multiple places (e.g., avatar, comments, profile), a normalized cache with automatic mutation propagation reduces stale-data bugs. Building this yourself means maintaining a manual entity store with subscription and notification logic.&lt;/p&gt;

&lt;p&gt;That's the role these libraries serve.&lt;/p&gt;

&lt;p&gt;Real-world REST applications pay similar costs, though they can sometimes defer this complexity longer depending on access patterns. TanStack Query, Axios, and SWR introduce additional bundle size for features like caching and request management.&lt;/p&gt;

&lt;p&gt;A fairer comparison is either naive-to-naive (no clients, just &lt;code&gt;fetch()&lt;/code&gt;) or full-featured to full-featured: Apollo Client and tools like TanStack Query both add bundle size in exchange for caching, deduplication, and state management.&lt;/p&gt;
&lt;h3&gt;
  
  
  Claim 11: "fetch() ships with every browser at 0 KB"
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Verdict: Confirmed.
&lt;/h4&gt;

&lt;p&gt;True. The Fetch API is built into all modern browsers and is available natively in recent versions of Node.js. But as we showed in Claim 10, GraphQL also works with &lt;code&gt;fetch()&lt;/code&gt; at 0 KB. This is often presented as a REST advantage, even though it applies equally to both.&lt;/p&gt;
&lt;h2&gt;
  
  
  Is GraphQL Less Secure Than REST?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Claim 12: "A 128-byte nested query can consume 10 seconds of CPU time"
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Verdict: Partially true (context-dependent).
&lt;/h4&gt;

&lt;p&gt;Security research shows that small, deeply nested GraphQL queries can consume disproportionate CPU time.&lt;/p&gt;

&lt;p&gt;This applies primarily to resolver-based implementations without depth or cost controls. In a GraphQL schema with circular references (e.g., a &lt;code&gt;User&lt;/code&gt; has &lt;code&gt;friends&lt;/code&gt; who are also &lt;code&gt;User&lt;/code&gt; objects), an attacker can construct a query like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a worst case scenario with an unoptimized implementation, if each user has 10 friends, 6 levels of nesting could theoretically result in up to 10^6 = 1,000,000 resolver calls. The query string itself is tiny, but the computational cost can grow quickly — in some cases exponentially — depending on schema design and resolver behavior.&lt;/p&gt;

&lt;p&gt;The specific numbers depend on schema complexity and server hardware, but the principle is documented in &lt;a href="https://medium.com/@ibm_ptc_security/denial-of-service-attacks-with-graphql-77189a6ba85b" rel="noopener noreferrer"&gt;security writeups and research&lt;/a&gt; .&lt;/p&gt;

&lt;h4&gt;
  
  
  Scope: first-party vs public APIs matters.
&lt;/h4&gt;

&lt;p&gt;For first-party APIs, trusted documents can significantly reduce this attack vector in controlled environments, but do not address inefficient resolver behavior within allowed operations. When a GraphQL server only executes pre-registered operations, arbitrary queries, including malicious depth attacks, can be rejected before execution. The &lt;a href="https://graphql.org/learn/security/" rel="noopener noreferrer"&gt;official GraphQL.org Security page&lt;/a&gt;  confirms this.&lt;/p&gt;

&lt;p&gt;For public APIs, this remains a real concern. GraphQL.org explicitly states that "trusted documents can't be used for public APIs because the operations sent by third-party clients won't be known in advance." Public APIs need depth limiting, cost analysis, and rate limiting. Many implementations also use batching techniques (e.g., DataLoader) to reduce redundant resolver execution.&lt;/p&gt;

&lt;p&gt;The attack scenario is valid for public APIs. But many GraphQL deployments are first-party, and persisted queries can prevent arbitrary depth attacks by restricting execution to pre-registered operations. These risks depend heavily on whether the API accepts arbitrary queries or restricts execution to known operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 13: "80% of GraphQL APIs are vulnerable to denial-of-service through query depth"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Misleading framing (misinterpreted statistic).
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;80%&lt;/strong&gt; figure is commonly misinterpreted from &lt;a href="https://escape.tech/blog/the-state-of-graphql-security-2024/" rel="noopener noreferrer"&gt;Escape's State of GraphQL Security 2024&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;The reported DoS vulnerability rate is &lt;strong&gt;69%&lt;/strong&gt; of scanned public GraphQL endpoints. &lt;strong&gt;80%&lt;/strong&gt; comes from a separate finding in the same report: "&lt;strong&gt;80%&lt;/strong&gt; of issues could have been resolved by implementing best practices", which measures remediability, not prevalence.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;69%&lt;/strong&gt; and &lt;strong&gt;80%&lt;/strong&gt; figures come from different parts of the same report and measure different things; &lt;strong&gt;they are not interchangeable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Public APIs accepting arbitrary third-party queries have a different security profile than first-party APIs using trusted documents. For first-party APIs, the depth-DoS attack vector can be significantly reduced when using persisted queries, though inefficient or overly expensive allowed operations can still create performance risks.&lt;/p&gt;

&lt;h4&gt;
  
  
  API security challenges are not unique to GraphQL.
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://salt.security/blog/increasing-api-traffic-proliferating-attack-activity-and-lack-of-maturity-key-findings-from-salt-securitys-2024-state-of-api-security-report" rel="noopener noreferrer"&gt;Salt Security's 2024 report&lt;/a&gt; , based on a survey of 250 IT and security professionals, found that &lt;strong&gt;37%&lt;/strong&gt; experienced an API security incident in the past 12 months (up from &lt;strong&gt;17%&lt;/strong&gt; in 2023), &lt;strong&gt;95%&lt;/strong&gt; encountered security issues in production APIs, and &lt;strong&gt;23%&lt;/strong&gt; suffered an API-related breach.&lt;/p&gt;

&lt;p&gt;These findings apply to APIs broadly and are not specific to GraphQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important caveat:&lt;/strong&gt; Escape's &lt;strong&gt;69%&lt;/strong&gt; measures vulnerability scan results on 160 public endpoints. Salt's figures measure self-reported incidents across 250 organizations. They are not directly comparable because different methodologies are being used to measure different things. Even so, the broader point is: API security is an industry-wide challenge, not specific to GraphQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 14: "Most frameworks ship with no default depth limit"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Partially true (implementation defaults).
&lt;/h4&gt;

&lt;p&gt;Many GraphQL server frameworks do not enable depth limiting by default. The &lt;a href="https://graphql.org/learn/security/" rel="noopener noreferrer"&gt;official GraphQL.org Security page&lt;/a&gt;  recommends it, stating: "Even when the N+1 problem has been remediated through batched requests to underlying data sources, overly nested fields may still place excessive load on server resources." This is not defined at the GraphQL specification level and is left to server implementations.&lt;/p&gt;

&lt;h4&gt;
  
  
  This is a real concern and should be taken seriously, even with DataLoader or query compilation.
&lt;/h4&gt;

&lt;p&gt;The plugin ecosystem makes it relatively easy to add these controls. Libraries such as &lt;a href="https://github.com/Escape-Technologies/graphql-armor" rel="noopener noreferrer"&gt;GraphQL Armor&lt;/a&gt;  and &lt;a href="https://the-guild.dev/graphql/envelop" rel="noopener noreferrer"&gt;Envelop&lt;/a&gt;  plugins provide depth and cost controls. But it is opt-in, not opt-out.&lt;/p&gt;

&lt;p&gt;However, the comparison should be symmetric.&lt;/p&gt;

&lt;p&gt;Many REST frameworks also ship with limited security controls enabled by default. &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express.js&lt;/a&gt; , a minimal web framework, does not include rate limiting or input validation out of the box and relies on middleware for these concerns. &lt;a href="https://www.django-rest-framework.org/" rel="noopener noreferrer"&gt;Django REST Framework&lt;/a&gt;  includes throttling features, but they are not enabled by default.&lt;/p&gt;

&lt;p&gt;"Insecure defaults" is not a GraphQL-specific problem; it is a common pattern across web frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP Behavior and Error Handling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Claim 15: "GraphQL returns HTTP 200 always, even when errors occur"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Misleading framing.
&lt;/h4&gt;

&lt;p&gt;This conflates GraphQL (the query language and execution engine) with one specific transport binding.&lt;/p&gt;

&lt;p&gt;GraphQL is designed to be transport-independent. It runs over HTTP, WebSockets, Server-Sent Events, multipart responses, and other transport mechanisms. &lt;strong&gt;This is a deliberate architectural choice.&lt;/strong&gt; GraphQL is similar to SQL in that it is a query language and does not dictate how queries are transported. You can run SQL over a TCP socket, a named pipe, or an HTTP API. GraphQL works the same way.&lt;/p&gt;

&lt;p&gt;GraphQL reports &lt;code&gt;errors&lt;/code&gt; in the response body's errors array rather than relying solely on transport-level status codes. A single GraphQL response can contain both &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;errors&lt;/code&gt;, representing partial success.&lt;/p&gt;

&lt;p&gt;The current working draft of the &lt;a href="https://spec.graphql.org/draft/#sec-Handling-Field-Errors" rel="noopener noreferrer"&gt;GraphQL specification&lt;/a&gt;  defines this explicitly: a field that errors out returns &lt;code&gt;null&lt;/code&gt; and adds an entry to the &lt;code&gt;errors&lt;/code&gt; array, while sibling fields resolve successfully.&lt;/p&gt;

&lt;p&gt;This is a defined behavior of the GraphQL execution model rather than a limitation. HTTP status codes do not cleanly express partial success: a request is typically represented as either 200 OK or 4xx/5xx, but not both.&lt;/p&gt;

&lt;p&gt;GraphQL can tell a client "here's &lt;strong&gt;90%&lt;/strong&gt; of what you asked for, and here's exactly which field failed and why." In practice, many GraphQL-over-HTTP implementations return HTTP 200 responses with errors encoded in the response body. However, transport-level errors (e.g., invalid requests, authentication failures, or server errors) may still return appropriate non-200 status codes.&lt;/p&gt;

&lt;p&gt;Under the commonly used &lt;code&gt;application/json&lt;/code&gt; media type, the &lt;a href="https://graphql.github.io/graphql-over-http/" rel="noopener noreferrer"&gt;GraphQL-over-HTTP specification&lt;/a&gt;  recommends returning 200 OK for any well‑formed GraphQL response, even when the errors field is populated. More nuanced status codes, including non-200 responses, are defined when using the &lt;code&gt;application/graphql-response+json&lt;/code&gt; media type.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 16: "The OWASP GraphQL Cheat Sheet reads like a confession of design decisions that were never made"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Misleading framing (not unique to GraphQL).
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP GraphQL Cheat Sheet&lt;/a&gt;  documents standard security hardening practices, similar to other OWASP cheat sheets. But OWASP publishes similar cheat sheets for many technologies.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;REST Security Cheat Sheet&lt;/a&gt;  covers input validation, output encoding, HTTPS enforcement, access control, rate limiting, and error handling. These are not defined by REST itself, but are implemented at the framework or application level. The &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/REST_Assessment_Cheat_Sheet.html" rel="noopener noreferrer"&gt;REST Assessment Cheat Sheet&lt;/a&gt;  goes further, documenting how to test REST APIs for vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://owasp.org/" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt;  also publishes cheat sheets for &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; , &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Django_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;Django&lt;/a&gt; , &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html" rel="noopener noreferrer"&gt;Ruby on Rails&lt;/a&gt; , and dozens of other technologies.&lt;/p&gt;

&lt;p&gt;Characterizing the GraphQL cheat sheet as a "confession" while ignoring equivalent guidance for REST can imply that GraphQL is uniquely insecure compared to other technologies. The existence of an OWASP cheat sheet reflects community attention to security and ecosystem maturity, rather than indicating an inherent design flaw in the technology itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adoption and Tooling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Claim 17: "83% of web services use REST"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Misleading framing (usage ≠ exclusivity).
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;83%&lt;/strong&gt; figure is often attributed to the RapidAPI Developer Survey 2024, which other sources describe as measuring public APIs using REST, but I was not able to locate the original survey report directly. The &lt;a href="https://www.postman.com/state-of-api/2025/" rel="noopener noreferrer"&gt;Postman 2025 State of the API Report&lt;/a&gt;  reports REST usage at &lt;strong&gt;93%&lt;/strong&gt; among surveyed developers, not &lt;strong&gt;83%&lt;/strong&gt;. Postman's survey allows multiple selections, so &lt;strong&gt;93%&lt;/strong&gt; reflects developers who use REST, not developers who use REST exclusively.&lt;/p&gt;

&lt;p&gt;The same survey shows &lt;strong&gt;33%&lt;/strong&gt; of developers use GraphQL alongside REST, making it one of the most commonly used API styles in the survey. Multiple factors contribute to REST's widespread use beyond active evaluation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Historical momentum:&lt;/strong&gt; REST has been the default since the mid-2000s. Most APIs were built before GraphQL was available or widely adopted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GraphQL adoption is increasing&lt;/strong&gt;: &lt;a href="https://www.gartner.com/en/documents/7488253" rel="noopener noreferrer"&gt;Gartner has predicte&lt;/a&gt;d  that by 2027, more than &lt;strong&gt;60%&lt;/strong&gt; of enterprises will use GraphQL in production, up from less than &lt;strong&gt;30%&lt;/strong&gt; in 2024.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;They coexist:&lt;/strong&gt; Companies such as GitHub, Shopify, Netflix, Airbnb, Twitter, and The New York Times run GraphQL APIs in production, often alongside REST.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using REST's market share as evidence against GraphQL reflects historical adoption patterns more than a direct comparison of technical fit or capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim 18: "REST with OpenAPI 3.0 offers self-documenting, typed client generation, HTTP caching built in"
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Verdict: Misleading framing (omits comparable GraphQL capabilities).
&lt;/h4&gt;

&lt;p&gt;Everything stated about OpenAPI is true. However, GraphQL has comparable capabilities in each area, with different trade-offs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-documenting:&lt;/strong&gt; A GraphQL server requires a GraphQL schema. The schema is not optional, not a nice-to-have, not a separate file you maintain on the side. It is a required part of the server.&lt;/p&gt;

&lt;p&gt;GraphQL servers can expose their full type system through &lt;a href="https://graphql.org/learn/introspection/" rel="noopener noreferrer"&gt;introspection&lt;/a&gt; , which lets any client query available types, fields, and arguments at runtime. Meta's &lt;a href="https://engineering.fb.com/2015/09/14/core-infra/graphql-a-data-query-language/" rel="noopener noreferrer"&gt;engineering blog&lt;/a&gt;  describes this as a core design goal: "Introspective: A GraphQL server can be queried for the types it supports."&lt;/p&gt;

&lt;p&gt;With REST, an OpenAPI spec is optional. You often need to generate or maintain the spec separately and keep it in sync with your implementation. Some REST APIs still ship without a machine-readable description. An OpenAPI spec can drift from the implementation because they are separate artifacts.&lt;/p&gt;

&lt;p&gt;In GraphQL, the schema is part of the server itself, which in practice can make drift much harder, because the schema is tied directly to the running implementation, though that is not guaranteed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Typed client generation
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://the-guild.dev/graphql/codegen" rel="noopener noreferrer"&gt;GraphQL Codegen&lt;/a&gt;  (by The Guild) and tools like &lt;a href="https://github.com/Khan/genqlient" rel="noopener noreferrer"&gt;genqlient&lt;/a&gt;  produce compile-time validated, fully typed client code. &lt;a href="https://graphql.org/blog/2024-09-19-codegen/" rel="noopener noreferrer"&gt;GraphQL.org's blog&lt;/a&gt;  documents that operation-based codegen produces types specific to each query, which can be more precise than endpoint-level types because you get types matching exactly the fields you requested.&lt;/p&gt;

&lt;p&gt;OpenAPI also supports typed client generation, but typically relies on maintaining a separate specification. In many cases, OpenAPI-based clients are simpler to generate and require less build-time integration.&lt;/p&gt;

&lt;h4&gt;
  
  
  HTTP caching
&lt;/h4&gt;

&lt;p&gt;As discussed in Claims 8-9, persisted queries can enable HTTP caching for GraphQL. And as discussed in Claim 5, REST's HTTP caching can be less effective for some relational or batch-style queries, which is the use case being compared.&lt;/p&gt;

&lt;p&gt;By default, GraphQL does not align as directly with HTTP caching as REST.&lt;/p&gt;

&lt;h4&gt;
  
  
  Totals (by verdict): 3 confirmed, 6 partially true, and 9 misleading or incomplete.
&lt;/h4&gt;

&lt;p&gt;In practice, REST remains a strong default for simpler, resource‑oriented services, while GraphQL shines in complex, client‑driven, multi‑resource scenarios. The best choice depends on the workload, not on which protocol is “better” in the abstract.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part II: What the Evidence Means
&lt;/h3&gt;

&lt;p&gt;This section is opinionated. Part I was about what the sources say. This part is about what I think it means for the industry based on my experience building API infrastructure for the past several years. Take it as one perspective among many.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where REST Genuinely Wins
&lt;/h2&gt;

&lt;p&gt;REST is the right choice for a lot of problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple CRUD with flat resources.&lt;/li&gt;
&lt;li&gt;Public APIs where every resource has a stable URL.&lt;/li&gt;
&lt;li&gt;Systems where HTTP caching on individual endpoints matters.&lt;/li&gt;
&lt;li&gt;Scenarios where the data model is straightforward, and each client needs more or less the same response shape.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;REST's ecosystem is mature, and every developer knows that. The tooling is battle-tested. OpenAPI provides typed client generation and machine-readable documentation. For these use cases, adding GraphQL would be adding complexity without proportional benefit.&lt;/p&gt;

&lt;p&gt;If you have 12 REST endpoints serving flat resources to a single frontend, and your team is productive, keep what you have.&lt;/p&gt;

&lt;p&gt;If you have a single frontend talking to a single backend, and both are TypeScript, do you need REST at all? Tools like &lt;a href="https://trpc.io/" rel="noopener noreferrer"&gt;tRPC&lt;/a&gt;  give you end-to-end type safety with zero schema definition, no OpenAPI spec, and no code generation step. Just TypeScript functions on the server, typed calls on the client. For that specific architecture, tRPC is simpler than both REST and GraphQL. The "12 endpoints and a fetch() call" scenario might not need REST any more than it needs GraphQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where GraphQL Genuinely Wins
&lt;/h2&gt;

&lt;p&gt;The problems GraphQL was designed to solve are real, and REST still has no good answer for them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relational data across multiple sources.&lt;/strong&gt; When a single client view needs data from users, orders, products, and reviews, GraphQL fetches it in one typed request. REST either makes multiple round-trips or requires purpose-built batch endpoints that are bespoke, uncacheable, and expensive to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-describing schemas and codegen.&lt;/strong&gt; The GraphQL schema is the documentation. Because it is tightly coupled to the implementation, that makes drift much harder in practice. &lt;a href="https://the-guild.dev/graphql/codegen" rel="noopener noreferrer"&gt;GraphQL Codegen&lt;/a&gt;  produces compile-time validated, fully typed client code specific to each query. OpenAPI can do similar things, but requires maintaining a separate spec file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile and bandwidth-constrained clients.&lt;/strong&gt; This is the original problem Facebook solved in 2012. Clients request exactly the fields they need. No over-fetching and no under-fetching. The peer-reviewed study by (&lt;a href="https://dl.acm.org/doi/10.1145/3357141.3357149" rel="noopener noreferrer"&gt;Seabra et al.&lt;/a&gt; ) found that GraphQL improved performance in two out of three tested applications under moderate workloads, but it did not outperform REST consistently, especially under higher load.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Federation Changes the Question Entirely
&lt;/h2&gt;

&lt;p&gt;You may see the argument framed as "12 REST endpoints versus GraphQL complexity." That framing assumes your API surface stays small. For many organizations, it won't.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql.org/learn/federation/" rel="noopener noreferrer"&gt;GraphQL Federation&lt;/a&gt;  allows organizations to start with a single monolithic GraphQL API and gradually decompose it into independently owned subgraphs as teams grow. All subgraphs compose into a unified supergraph that clients consume as one schema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/federation" rel="noopener noreferrer"&gt;Apollo Federation&lt;/a&gt;  pioneered this pattern, and today multiple vendors provide production-ready Federation routers: &lt;a href="https://www.apollographql.com/docs/graphos/routing" rel="noopener noreferrer"&gt;Apollo Router&lt;/a&gt;  (Rust), &lt;a href="https://cosmo-docs.wundergraph.com/overview" rel="noopener noreferrer"&gt;WunderGraph Cosmo Router&lt;/a&gt;  (Go, open-source Apache 2.0), &lt;a href="https://the-guild.dev/graphql/hive" rel="noopener noreferrer"&gt;The Guild's Hive Gateway&lt;/a&gt;  (TypeScript), &lt;a href="https://chillicream.com/" rel="noopener noreferrer"&gt;ChilliCream's Hot Chocolate&lt;/a&gt;  (C#/.NET), and &lt;a href="https://aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AWS AppSync&lt;/a&gt;  (managed service). Competition between vendors means teams have choices without being locked into a single provider.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/graphql/composite-schemas-wg" rel="noopener noreferrer"&gt;Composite Schemas Working Group&lt;/a&gt; , an official subcommittee of the GraphQL Foundation, is working to standardize how subgraph schemas compose into a unified API.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://graphql.github.io/composite-schemas-spec/" rel="noopener noreferrer"&gt;specification&lt;/a&gt;  defines vendor-neutral composition and distributed execution rules, so that subgraphs written against the spec work with any compliant router. This is Federation becoming an open standard, not a single vendor's product.&lt;/p&gt;

&lt;p&gt;REST has no equivalent. When REST-based organizations need to compose multiple services behind a unified API, they choose between API Gateways (vendor-specific routing), BFF patterns (bespoke aggregation code per frontend), or OpenAPI merging (flat endpoint lists with no cross-service entity relationships). Each approach is proprietary, and none handles the question: "A User from Service A has Orders from Service B."&lt;/p&gt;

&lt;h4&gt;
  
  
  Federation answers that question by design.
&lt;/h4&gt;

&lt;p&gt;And here is what I think many people miss: &lt;strong&gt;you can have Federation as the composition layer and REST as the consumption layer.&lt;/strong&gt; Build a federated supergraph to unify APIs across teams. Then expose a REST API on top for clients that want it. This is a pattern we see growing in enterprise deployments. Federation and REST are not opposing choices. They operate at different levels of the stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Agents Need Structured, Self-Describing APIs
&lt;/h2&gt;

&lt;p&gt;The API consumer is changing. Increasingly, the client calling your API is an &lt;a href="https://wundergraph.com/blog/graphql-api-layer-for-ai-agents" rel="noopener noreferrer"&gt;AI agent&lt;/a&gt; , not a human developer reading docs.&lt;/p&gt;

&lt;p&gt;Agents need to discover capabilities, understand types, and request specific fields, all within a limited context window. GraphQL's typed, introspectable schema was built for exactly this interaction pattern. An agent can query the schema to understand what's available, then construct a precise request for exactly the data it needs.&lt;/p&gt;

&lt;p&gt;REST with OpenAPI can serve agents, too. But exposing thousands of REST endpoints to an agent overwhelms its context window. A structured graph lets agents search and select precisely what they need.&lt;/p&gt;

&lt;p&gt;The industry is moving in this direction. The &lt;a href="https://graphql.org/blog/2026-01-20-recap-nov-2025-ai-wg/" rel="noopener noreferrer"&gt;GraphQL AI Working Group&lt;/a&gt;  is developing standards for how LLM agents interact with GraphQL APIs.&lt;/p&gt;

&lt;p&gt;That said, AI agents will also call REST APIs. Tools like MCP are protocol-agnostic.&lt;/p&gt;

&lt;p&gt;The question is how you organize your APIs so agents can find what they need. A federated supergraph provides that organization. A flat list of REST endpoints does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Right Question Is Not Which One Wins
&lt;/h2&gt;

&lt;p&gt;The REST vs. GraphQL debate keeps producing articles that pick one side and build the case. Some pick REST. Plenty of others pick GraphQL. Both sides select favorable benchmarks, ignore inconvenient tradeoffs, and declare the other technology dead or dying.&lt;/p&gt;

&lt;p&gt;The evidence does not support either extreme.&lt;/p&gt;

&lt;p&gt;REST at &lt;strong&gt;93%&lt;/strong&gt; adoption (&lt;a href="https://www.postman.com/state-of-api/2025/" rel="noopener noreferrer"&gt;Postman 2025&lt;/a&gt; ) is not under threat from GraphQL. GraphQL, with &lt;a href="https://www.gartner.com/en/documents/7488253" rel="noopener noreferrer"&gt;Gartner predicting 60%+ enterprise adoption by 2027&lt;/a&gt;  and &lt;strong&gt;33%&lt;/strong&gt; of developers already using it alongside REST, is not a niche technology. They coexist because they solve different problems.&lt;/p&gt;

&lt;p&gt;The right question is: what shape is your data, who are your consumers, and how will your API surface grow?&lt;/p&gt;

&lt;p&gt;For public APIs where third-party developers need to integrate: REST. REST makes it easy to generate SDKs in any language, and every developer already knows how to call a REST endpoint. For flat resources, stable URLs, and simple CRUD: also REST. For a single TypeScript frontend and backend: maybe tRPC. For relational data, multiple consumers with different needs, and cross-team composition: GraphQL. For organizations scaling from one team to many: Federation. For all of the above at the same time: both.&lt;/p&gt;

&lt;p&gt;The next time you read a blog post that makes specific technical claims about either technology (including one of my posts), check the sources. Read the original documentation. Look at the methodology behind the benchmarks. The evidence is out there. Use it.&lt;/p&gt;

&lt;p&gt;This article was originally published on the &lt;a href="https://wundergraph.com/blog/fact-checking-graphql-vs-rest" rel="noopener noreferrer"&gt;WunderGraph blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>backend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How the Fission Algorithm Works: Top-Down GraphQL Federation Design</title>
      <dc:creator>WunderGraph</dc:creator>
      <pubDate>Wed, 15 Apr 2026 17:53:42 +0000</pubDate>
      <link>https://forem.com/wundergraph/how-the-fission-algorithm-works-top-down-graphql-federation-design-507n</link>
      <guid>https://forem.com/wundergraph/how-the-fission-algorithm-works-top-down-graphql-federation-design-507n</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;Fission inverts GraphQL Federation. Instead of composing subgraphs into a supergraph, you design the supergraph first and Fission decomposes it into subgraph specs, handling entity keys, directives, and validation automatically.&lt;/p&gt;




&lt;p&gt;In a &lt;a href="https://wundergraph.com/blog/graphql-federation-was-built-backwards" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;, I argued that Federation's composition model is backwards. It builds the supergraph from the bottom up when it should be designed from the top down.&lt;/p&gt;

&lt;p&gt;This post is a technical deep dive into how the Fission algorithm actually does that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Fission Solves
&lt;/h2&gt;

&lt;p&gt;To understand Fission, first you need to understand what makes schema changes expensive in Federation.&lt;/p&gt;

&lt;p&gt;Consider a federated graph with three subgraphs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Subgraph: Users&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Subgraph: Orders&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;PENDING&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;SHIPPED&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;DELIVERED&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Subgraph: Products&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Composition produces a supergraph where you can query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a frontend team wants to add shipping tracking. This is their &lt;a href="https://wundergraph.com/blog/dream-query-design-apis-from-consumer-out" rel="noopener noreferrer"&gt;dream query&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;shipping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;trackingNumber&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;estimatedDelivery&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;currentLocation&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the traditional workflow, the frontend team has to figure out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;shipping&lt;/code&gt; should be a field on &lt;code&gt;Order&lt;/code&gt;, but which subgraph owns &lt;code&gt;Order&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Both the Orders subgraph and the Products subgraph define &lt;code&gt;Order&lt;/code&gt; as an entity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;carrier&lt;/code&gt; and &lt;code&gt;trackingNumber&lt;/code&gt; come from the shipping provider.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;estimatedDelivery&lt;/code&gt; might come from logistics.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;currentLocation&lt;/code&gt; requires real-time tracking data.&lt;/li&gt;
&lt;li&gt;Should this be a new subgraph? An extension of Orders? Both?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frontend team can't answer these questions, so the platform team needs to be brought in. Meetings are scheduled and the process begins.&lt;/p&gt;

&lt;p&gt;Fission automates the hardest parts of this process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Fission Actually Is
&lt;/h2&gt;

&lt;p&gt;Before we walk through an example, it's important to understand what Fission is and what it isn't.&lt;/p&gt;

&lt;p&gt;Fission is not just the opposite of composition, a batch algorithm that takes a supergraph and splits it into subgraphs. It's a &lt;strong&gt;stateful schema graph engine&lt;/strong&gt;, an in-memory system that maintains two synchronized views of your federated graph at all times:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The supergraph view&lt;/strong&gt; — the unified, consumer-facing schema.
This is the canonical source of truth for what the API looks like.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The subgraph views&lt;/strong&gt; — one per service, representing what each team
is responsible for implementing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These two views are kept in sync. When you make a change to either one, Fission will automatically propagate the consequences: renaming a type cascades to every subgraph and every field reference. Adding a type to a subgraph pulls in all its transitive dependencies. Entity &lt;code&gt;@key&lt;/code&gt; directives propagate automatically.&lt;br&gt;
&lt;code&gt;@shareable&lt;/code&gt; directives are added bidirectionally when fields exist in multiple subgraphs.&lt;/p&gt;

&lt;p&gt;Every edit follows the same pattern: &lt;strong&gt;validate first, mutate second, update indexes, and return a structured result.&lt;/strong&gt; If validation fails, no state changes happen. This ensures you never have a half-applied edit.&lt;/p&gt;

&lt;p&gt;The key insight is the synchronization contract:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The supergraph view represents the union of all subgraph views,&lt;br&gt;
plus any types and fields not yet assigned to a subgraph.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means the architect can design the complete API shape first and then decide how to distribute the implementation across subgraphs.&lt;/p&gt;

&lt;p&gt;The supergraph doesn't have to be assembled from the bottom up.&lt;br&gt;
It can be designed from the top down.&lt;/p&gt;
&lt;h2&gt;
  
  
  How It Works in Practice
&lt;/h2&gt;

&lt;p&gt;Let's walk through the shipping example step by step.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Design on the Supergraph
&lt;/h3&gt;

&lt;p&gt;The architect adds the new types and fields directly on the supergraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# New additions to the supergraph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ShippingInfo&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# new field&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ShippingInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# new type&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;estimatedDelivery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;currentLocation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, &lt;code&gt;ShippingInfo&lt;/code&gt; exists in the supergraph but isn't assigned to any subgraph yet. The &lt;code&gt;shipping&lt;/code&gt; field on &lt;code&gt;Order&lt;/code&gt; is defined but no team is responsible for implementing it.&lt;/p&gt;

&lt;p&gt;The supergraph is the API you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Assign to Subgraphs
&lt;/h3&gt;

&lt;p&gt;The architect assigns the new type to an existing subgraph, or if necessary, creates a new one.&lt;/p&gt;

&lt;p&gt;Let's say they assign &lt;code&gt;ShippingInfo&lt;/code&gt; and the &lt;code&gt;shipping&lt;/code&gt; field on &lt;code&gt;Order&lt;/code&gt; to a new "Shipping" subgraph.&lt;/p&gt;

&lt;p&gt;When this happens, Fission automatically does several things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transitive dependency propagation:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ShippingInfo&lt;/code&gt; references &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;DateTime&lt;/code&gt;, and &lt;code&gt;Boolean&lt;/code&gt;.&lt;br&gt;
These are all scalars that are already available. But if &lt;code&gt;ShippingInfo&lt;/code&gt; had a field like &lt;code&gt;warehouse: Warehouse!&lt;/code&gt;, and the &lt;code&gt;Warehouse&lt;/code&gt; type doesn't exist in the Shipping subgraph yet, Fission will automatically pull it in, along with anything &lt;code&gt;Warehouse&lt;/code&gt; references. This propagation is cycle-safe; mutual type references don't cause infinite loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entity key propagation:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Order&lt;/code&gt; is an entity with &lt;code&gt;@key(fields: "id")&lt;/code&gt; in the Orders and Products subgraphs. When the Shipping subgraph extends &lt;code&gt;Order&lt;/code&gt;, Fission automatically adds &lt;code&gt;@key(fields: "id")&lt;/code&gt; to the Shipping subgraph's &lt;code&gt;Order&lt;/code&gt; definition. The entity remains resolvable across subgraph boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shareable propagation:&lt;/strong&gt;&lt;br&gt;
If the &lt;code&gt;shipping&lt;/code&gt; field ends up defined in multiple V2 subgraphs, &lt;code&gt;@shareable&lt;/code&gt; is added bidirectionally to &lt;em&gt;all&lt;/em&gt; subgraphs containing that field, not just the new one. This ensures the rendered SDL is always valid for composition.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Continuous Validation
&lt;/h3&gt;

&lt;p&gt;Every edit is validated before it's applied.&lt;/p&gt;

&lt;p&gt;Fission enforces a comprehensive set of invariants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reference integrity:&lt;/strong&gt; you cannot remove a type that's still referenced by other fields.
The error includes the exact coordinates that still reference it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last-child protection:&lt;/strong&gt; you cannot remove the last field from a type
that's still referenced elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key field validity:&lt;/strong&gt; you cannot rename or change the type of a field that participates in a &lt;code&gt;@key&lt;/code&gt; field set. The key selection tree would break.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-kind compatibility:&lt;/strong&gt; you cannot have &lt;code&gt;User&lt;/code&gt; as an Object in one subgraph and an Interface in another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface consistency:&lt;/strong&gt; when a type implements an interface, it must have all the interface's fields with compatible types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any validation fails, the edit is rejected and the state remains unchanged. The error message includes enough information for the Hub UI to show exactly what went wrong and suggest a fix.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cascading Renames
&lt;/h3&gt;

&lt;p&gt;Renaming is the most complex operation, and it's where Fission's&lt;br&gt;
reference tracking really shines.&lt;/p&gt;

&lt;p&gt;When a type is renamed at the supergraph level — say &lt;code&gt;Order&lt;/code&gt; becomes &lt;code&gt;CustomerOrder&lt;/code&gt; — Fission cascades the rename:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every subgraph containing &lt;code&gt;Order&lt;/code&gt; has its node renamed&lt;/li&gt;
&lt;li&gt;Every field typed as &lt;code&gt;Order&lt;/code&gt; or &lt;code&gt;[Order!]!&lt;/code&gt; is updated&lt;/li&gt;
&lt;li&gt;Every &lt;code&gt;@key&lt;/code&gt; field set referencing &lt;code&gt;Order&lt;/code&gt; is regenerated from its selection tree&lt;/li&gt;
&lt;li&gt;Interface implementations and union memberships are updated&lt;/li&gt;
&lt;li&gt;All reverse indexes are re-keyed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The SDL is not stored as text, it's regenerated from structured state. This means &lt;code&gt;@key(fields: "id")&lt;/code&gt; always reflects the current field names. If you rename the &lt;code&gt;id&lt;/code&gt; field to &lt;code&gt;orderId&lt;/code&gt;, the key directive automatically becomes &lt;code&gt;@key(fields: "orderId")&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Multi-Team Example
&lt;/h2&gt;

&lt;p&gt;Let's trace through a more complex scenario to see how these steps work together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Starting state:&lt;/strong&gt; the three subgraphs from above (Users, Orders, Products).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dream query:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$orderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="c"&gt;# new&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;inStock&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;warehouseLocation&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;shipping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="c"&gt;# new&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;trackingNumber&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;estimatedDelivery&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="c"&gt;# new&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;last4&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;receiptUrl&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three new capabilities: inventory, shipping, and payment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Design on the supergraph&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The architect adds all new types and fields to the supergraph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Product.inventory: InventoryInfo!&lt;/code&gt; (new field on existing entity)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;InventoryInfo&lt;/code&gt; with &lt;code&gt;inStock&lt;/code&gt; and &lt;code&gt;warehouseLocation&lt;/code&gt; (new type)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Order.shipping: ShippingInfo!&lt;/code&gt; (new field on existing entity)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ShippingInfo&lt;/code&gt; with &lt;code&gt;carrier&lt;/code&gt;, &lt;code&gt;trackingNumber&lt;/code&gt;, &lt;code&gt;estimatedDelivery&lt;/code&gt; (new type)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Order.payment: PaymentInfo!&lt;/code&gt; (new field on existing entity)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PaymentInfo&lt;/code&gt; with &lt;code&gt;method&lt;/code&gt;, &lt;code&gt;last4&lt;/code&gt;, &lt;code&gt;receiptUrl&lt;/code&gt; (new type)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Query.order(id: ID!): Order&lt;/code&gt; (new root field)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, the supergraph describes the ideal API. None of these new types are assigned to subgraphs yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Assign to subgraphs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The architect assigns each new type to a subgraph on the Hub canvas:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;New item&lt;/th&gt;
&lt;th&gt;Assigned to&lt;/th&gt;
&lt;th&gt;What Fission automates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;InventoryInfo&lt;/code&gt; + &lt;code&gt;Product.inventory&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;New Inventory subgraph&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Product @key(fields: "id")&lt;/code&gt; is propagated to the Inventory subgraph. Product entity is automatically resolvable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ShippingInfo&lt;/code&gt; + &lt;code&gt;Order.shipping&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;New Shipping subgraph&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Order @key(fields: "id")&lt;/code&gt; is propagated. If &lt;code&gt;ShippingInfo&lt;/code&gt; referenced other types, they'd be pulled in transitively.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PaymentInfo&lt;/code&gt; + &lt;code&gt;Order.payment&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;New Payments subgraph&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Order @key(fields: "id")&lt;/code&gt; is propagated.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Query.order&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Orders subgraph&lt;/td&gt;
&lt;td&gt;Orders already owns the &lt;code&gt;Order&lt;/code&gt; entity. Field is added, references are tracked.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The architect decides that &lt;code&gt;Product.inventory&lt;/code&gt; goes to a new Inventory subgraph because the Products team doesn't have warehouse data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Continuous validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every assignment is validated as it happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entity keys are resolvable across the new subgraph boundaries&lt;/li&gt;
&lt;li&gt;No type-kind conflicts (e.g., &lt;code&gt;Order&lt;/code&gt; is an Object in all subgraphs)&lt;/li&gt;
&lt;li&gt;Naming conventions pass (Hub's governance layer flags &lt;code&gt;inStock&lt;/code&gt; — should it be &lt;code&gt;isInStock&lt;/code&gt;? Architect approves the exception)&lt;/li&gt;
&lt;li&gt;Reference integrity is maintained&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the architect tried to assign &lt;code&gt;ShippingInfo&lt;/code&gt; to a subgraph in a way that would break composition, the edit would be rejected with a specific error before any state changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Design to Subgraph Specs
&lt;/h2&gt;

&lt;p&gt;At any point, Fission can render valid GraphQL SDL from structured state. This means the output reflects every cascading update, renamed field, and propagated directive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subgraph SDL Specifications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For each affected subgraph, Fission renders the exact SDL that the team needs to implement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# New subgraph: Shipping&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ShippingInfo&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ShippingInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;carrier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;estimatedDelivery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# New subgraph: Payments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PaymentInfo&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PaymentInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;last4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;receiptUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# New subgraph: Inventory&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InventoryInfo&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InventoryInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;inStock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;warehouseLocation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Modified subgraph: Orders (new root field)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# new&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each new subgraph automatically includes &lt;code&gt;@key(fields: "id")&lt;/code&gt; on the entities it extends, because Fission propagated these from the existing subgraphs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supergraph Preview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fission can also render the full supergraph SDL at any time. The architect can verify that the dream query will actually work before any code is written. Because the supergraph is the source of truth, not a composition result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Decisions
&lt;/h2&gt;

&lt;p&gt;A few architectural choices in Fission that are worth explaining:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The supergraph is the source of truth.&lt;/strong&gt;&lt;br&gt;
In traditional Federation, the supergraph is a side effect of composition. In Fission, it's the canonical representation. Removing a type from a subgraph does not remove it from the supergraph. You can design the complete API shape first, then decide how to distribute implementation. This is backwards from composition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fission automates consequences, not decisions.&lt;/strong&gt;&lt;br&gt;
The architect decides where types and fields belong. Fission automates everything that follows: transitive dependency propagation, entity key propagation, &lt;code&gt;@shareable&lt;/code&gt; management, reference tracking, and cascading renames. The goal is to automate the mechanical work and surface the judgment calls to humans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validate first, mutate second.&lt;/strong&gt;&lt;br&gt;
Every edit validates all preconditions before making any state change. If validation fails, the state is unchanged. Because invalid states are caught at design time, the "composition fails after three teams implement their parts" scenario is prevented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Federation directives are structured state, not text.&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;@key&lt;/code&gt;, &lt;code&gt;@shareable&lt;/code&gt;, &lt;code&gt;@inaccessible&lt;/code&gt;, &lt;code&gt;@override&lt;/code&gt;, and &lt;code&gt;@provides&lt;/code&gt; are modeled as structured state with their own selection trees, indexes, and validation rules. When you rename a field that participates in &lt;code&gt;@key(fields: "id name")&lt;/code&gt;, the key field set is automatically updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fission tracks the full lifecycle.&lt;/strong&gt;&lt;br&gt;
From proposal to review to implementation to composition, the entire workflow is tracked in one place. When a subgraph team ships their implementation, it's checked against the original specification. The proposal isn't complete until all specifications are met and the final composition succeeds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;Fission is the algorithmic counterpart to Hub's collaboration model.&lt;/p&gt;

&lt;p&gt;Hub provides the canvas, the collaboration workflow, and the governance layer. Fission provides the ability to take a high-level design intent and translate it into concrete, actionable subgraph specifications.&lt;/p&gt;

&lt;p&gt;Together, they make it possible to &lt;a href="https://wundergraph.com/blog/design-like-a-monolith-implement-as-microservices" rel="noopener noreferrer"&gt;design like a monolith and implement as microservices&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The supergraph is designed as a coherent whole. The implementation is distributed across teams. The algorithm handles the translation between the two.&lt;/p&gt;

&lt;p&gt;If you want to see Fission in action, &lt;a href="https://wundergraph.com/events/virtual/introducing-fission" rel="noopener noreferrer"&gt;watch the webinar&lt;/a&gt;&lt;br&gt;
or &lt;a href="https://wundergraph.com/contact/sales" rel="noopener noreferrer"&gt;schedule a walkthrough&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>architecture</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
