<?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>DEV Community: WunderGraph</title>
    <description>The latest articles on DEV Community by WunderGraph (@wundergraph).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3854287%2Fb07c1ad4-cd45-47b1-989f-1003b77dbf6b.png</url>
      <title>DEV Community: WunderGraph</title>
      <link>https://dev.to/wundergraph</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wundergraph"/>
    <language>en</language>
    <item>
      <title>AI governance: 11 laws, frameworks, and assurance schemes—and 3 jobs they serve.</title>
      <dc:creator>WunderGraph</dc:creator>
      <pubDate>Mon, 28 Sep 2026 14:31:49 +0000</pubDate>
      <link>https://dev.to/wundergraph/ai-governance-11-laws-frameworks-and-assurance-schemes-and-3-jobs-they-serve-1lk4</link>
      <guid>https://dev.to/wundergraph/ai-governance-11-laws-frameworks-and-assurance-schemes-and-3-jobs-they-serve-1lk4</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;The EU AI Act delayed its high-risk-system duties (Chapter III) to December 2, 2027 for standalone systems and August 2, 2028 for systems inside regulated products. That delay does not pause the rest of the Act. AI literacy and the original Article 5 bans have applied since February 2, 2025; GPAI provider duties since August 2, 2025; and most Article 50 transparency duties (disclose the chatbot, mark synthetic content, notify about emotion recognition) from August 2, 2026. A ninth banned-practice category, Article 5(1) points (ba) and (bb), arrives December 2, 2026. The surrounding laws, frameworks, and assurance schemes do three different jobs, and a certificate in one does not establish compliance in another. ISO 42001 certification, by itself, is not EU AI Act compliance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deferred, not cancelled
&lt;/h2&gt;

&lt;p&gt;On August 2, 2026, many EU AI Act provisions began to apply. High-risk systems were the exception.&lt;/p&gt;

&lt;p&gt;The Digital Omnibus on AI &lt;a href="https://eur-lex.europa.eu/eli/reg/2026/1744/oj" rel="noopener noreferrer"&gt;(Regulation (EU) 2026/1744 )&lt;/a&gt; deferred the core Chapter III duties for high-risk AI systems. Standalone high-risk use cases (Annex III) now apply from December 2, 2027. High-risk AI embedded in already-regulated products (Annex I) now applies from August 2, 2028. The delay is real. It is not a general pause on the Act &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;33&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Omnibus got its final Council green light on June 29, 2026 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;1&lt;/a&gt;, was dated July 8, published in the Official Journal on July 24, and entered into force July 27, 2026 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxpkg5880ghgos2xiho41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxpkg5880ghgos2xiho41.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Digital Omnibus on AI pushed both Chapter III high-risk deadlines back. That deferral is limited to Chapter III. The Omnibus also makes other, non-deferral changes elsewhere in the Act.&lt;/p&gt;

&lt;p&gt;The Commission wanted the delay to start only once the supporting harmonised standards, or other support tools, were ready; however, co-legislators dropped that and locked the dates. December 2, 2027 arrives whether or not anything has been cited in the Official Journal &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;5&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;6&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's delayed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;High-risk standalone systems (Annex III): December 2, 2027&lt;/li&gt;
&lt;li&gt;High-risk systems inside regulated products (Annex I): August 2, 2028&lt;/li&gt;
&lt;li&gt;National AI sandboxes (Article 57): August 2, 2027&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What's already on the clock
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Original Article 5 bans and AI literacy: since February 2, 2025&lt;/li&gt;
&lt;li&gt;GPAI provider duties: since August 2, 2025&lt;/li&gt;
&lt;li&gt;Most Article 50 transparency duties (chatbots, synthetic-content marking, emotion-recognition notices): August 2, 2026&lt;/li&gt;
&lt;li&gt;Ninth banned-practice category (intimate imagery / CSAM): December 2, 2026&lt;/li&gt;
&lt;li&gt;GPAI models already placed on the market before August 2, 2025: comply by August 2, 2027&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who it still reaches
&lt;/h2&gt;

&lt;p&gt;If you stop at the delay, you will feel like you can relax. Don't.&lt;/p&gt;

&lt;p&gt;The Act is not only for companies headquartered in the EU. It can apply to providers placing AI systems or GPAI models on the EU market, deployers using systems in the EU, and certain providers or deployers outside the EU where an AI system's output is used in the Union. Headquarters is not the test; scope stays subject to &lt;a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj" rel="noopener noreferrer"&gt;Article 2 's&lt;/a&gt; exclusions, and it depends on the facts of the system and how it is used 3. Those exclusions cover systems used exclusively for national security, military, or defence purposes; purely personal, non-professional use; and some open-source releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else the Omnibus changed
&lt;/h2&gt;

&lt;p&gt;Most of the Article 50 transparency duties applied from August 2, 2026: disclose the chatbot, mark the synthetic content, and notify people about emotion recognition &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;33&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From December 2, 2026, a ninth banned-practice category arrives (Article 5(1) points (ba) and (bb)): systems that generate or manipulate non-consensual intimate material of an identifiable person, or child sexual abuse material. That is not a blanket ban on generative models. For providers, placing the system on the market or putting it into service is prohibited only where that generation or manipulation is the system's intended purpose, or a reasonably foreseeable and reproducible outcome without adequate safeguards. For deployers, use is prohibited only where they use the system for that purpose &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One other grace period: if you are a provider of an AI system — including a general-purpose AI system — that generates synthetic audio, image, video, or text, and you placed it on the market before August 2, 2026, new Article 111(4) gives you until December 2, 2026 to comply with Article 50(2)'s machine-readable marking obligation. It does not delay the rest of Article 50 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Penalties follow the duty
&lt;/h2&gt;

&lt;p&gt;No published analysis of the Omnibus reports a change to the Act's maximum penalty tiers (Article 99). A penalty can follow only once the underlying duty applies. The original Article 5 bans have been enforceable since February 2, 2025; the new Article 5(1) points (ba) and (bb) apply from December 2, 2026; and the deferred Chapter III duties apply on their new 2027 and 2028 dates &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The caps themselves still sit where they did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Article 5 prohibitions: up to €35M or 7% of global turnover&lt;/li&gt;
&lt;li&gt;Most other violations: up to €15M or 3%&lt;/li&gt;
&lt;li&gt;Incorrect, incomplete, or misleading information: up to €7.5M or 1%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whichever is higher in each case, unless you are an SME or a small mid-cap &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt;. The deferral buys time to build high-risk systems but won't pardon what's already been banned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Office's bigger beat
&lt;/h2&gt;

&lt;p&gt;The Omnibus also expands and centralizes some of the EU AI Office's market-surveillance and enforcement powers. Amended Article 75(1) makes the Office the only competent enforcer (exclusively competent) in two cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The same provider built both the general-purpose model and the system sitting on top of it, or two providers in the same undertaking (the same economic group) did.&lt;/li&gt;
&lt;li&gt;The AI system constitutes, or is integrated into, a very large online platform or search engine designated under the Digital Services Act.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That exclusive competence covers the providers of those systems. It covers deployers only when they are also the provider, or form part of the same undertaking &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Office's powers sit in Articles 75a to 75d: it can supervise and investigate, accept binding commitments, run a non-compliance procedure, and, under Article 75c(5), impose periodic penalty payments of up to 5% of average daily income or worldwide annual turnover in the preceding financial year, per day, to compel specified actions such as answering an information request or allowing an inspection &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;National authorities still handle everything outside that slice, including Annex I product systems, certain Annex III cases, and specified law-enforcement, border, financial, and justice systems. This is a new lever, not a delayed one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Anthropic implements the marking rule
&lt;/h2&gt;

&lt;p&gt;Anthropic announced that &lt;a href="https://support.claude.com/en/articles/16266773-how-claude-marks-ai-generated-content" rel="noopener noreferrer"&gt;Claude models launched on or after August 2, 2026 will carry machine-readable markings from launch&lt;/a&gt; : an imperceptible text watermark, plus C2PA-signed provenance metadata for PNG, JPG, and SVG. Anthropic says the text watermark travels with copied-and-pasted text and can survive some edits. They are also working to retrofit older models during the transition period, across supported Claude surfaces worldwide &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;7&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;8&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anthropic released a &lt;a href="https://claude.com/check-content" rel="noopener noreferrer"&gt;Claude content checker&lt;/a&gt; . It reads C2PA credentials in files &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;32&lt;/a&gt;; text-watermark detection is a separate API, in private preview to eligible organizations &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;7&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A detected mark is evidence that Claude may have processed content, not proof that Claude authored it. The absence of a detectable mark does not establish human authorship. Anthropic lists five reasons a mark may not show up: the model predates marking support; the text was heavily edited, paraphrased, translated, or mixed into other writing; the passage is too short to carry a reliable signal; the file's metadata was stripped by format conversion, re-saving, a screenshot, or some other means; or the content came from a platform, feature, or file type where that marking type isn't supported &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;7&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;8&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  GPAI, "safety components," and systems already in the field
&lt;/h2&gt;

&lt;p&gt;Two nearer dates survived the Omnibus untouched. General-purpose AI models placed on the market before August 2, 2025 still have to comply by August 2, 2027, which lands before either deferred high-risk date &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;New Article 6(1a) to (1c) also narrows what counts as a safety component, so some Annex I systems drop out of high-risk altogether rather than getting more time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6(1a):&lt;/strong&gt; systems used solely for non-safety-related user assistance, performance optimisation, service efficiency, automation, convenience, or quality control are out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6(1b):&lt;/strong&gt; if the system's failure or malfunctioning would endanger health and safety, it still qualifies, even with that carve-out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6(1c):&lt;/strong&gt; if a product only has to go through third-party assessment for other risks — in particular radio-spectrum or electromagnetic-interference risks that do not affect health and safety — that does not, by itself, meet the Article 6(1)(b) condition for treating it as high-risk &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a provider treats an Annex III system as not high-risk under the Article 6(3) exception (the derogation), Article 6(4) still requires documenting that assessment before placing the system on the market or putting it into service, and registering it in the EU database (Article 49(2)) &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt;. That is not a universal registration duty. It applies to that classification route. The Commission proposed narrowing it; Council and Parliament kept the documentation and registration. The Omnibus only trims the filing by deleting Annex VIII, section B, points 7 and 9 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;6&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Article 111(2) kept its shape, with one change. It applies to operators of high-risk systems already placed on the market or put into service before Chapter III starts to apply — but only if, from that date, those systems undergo significant changes in their designs. The cutoff is no longer a fixed August 2, 2026. It now tracks when Chapter III applies: December 2, 2027 for Annex III, August 2, 2028 for Annex I. Providers and deployers of high-risk systems intended for public authorities must still take the necessary steps to comply by August 2, 2030 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt;. If your estate is legacy or public-sector, those are your dates.&lt;/p&gt;

&lt;p&gt;None of these dates matter without a record of who changed what and when. That record has to live somewhere, and it's usually not where teams think to look.&lt;/p&gt;

&lt;p&gt;The deadline is only one document. Around it sit eleven overlapping laws, frameworks, and assurance schemes from different bodies. Most of them landed or were revised in the last two years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three jobs, not eleven labels
&lt;/h2&gt;

&lt;p&gt;Stop focusing on the framework and start with the job. There are three:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fylp7ga2r40i6bnbpuycm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fylp7ga2r40i6bnbpuycm.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These 11 laws, frameworks, and assurance schemes collapse into three jobs: prove it, defend it, build it.&lt;/p&gt;

&lt;p&gt;The expensive mistake is assuming a certificate in one satisfies another. All three jobs eventually need evidence from somewhere: who touched what, when, and who approved it.&lt;/p&gt;

&lt;p&gt;Three artifacts sit on more than one line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSA AICM&lt;/strong&gt; is a security-and-governance control framework. Its associated AI-CAIQ supports self-assessment and third-party evaluation, and CSA provides materials for STAR for AI Level 1 submissions 15.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NIST AI 600-1&lt;/strong&gt; reads as security content but sits inside a governance framework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AIUC-1&lt;/strong&gt; is a procurement instrument wearing security clothing. A consortium contributed the controls, but the publisher is an AI underwriting company, which is the tell.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reference table&lt;br&gt;
&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/1DSZ756xDszA2Vi12h-mAGdjxJACC0uB7Mlau7EdJAZ0/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%2FAHkbwyKv-dj8w2kGFV_wL4WKSb2xesVISN-HWSwOuaemRi70QozrbGK2VqsIidX9u6EHFUaPHyXs1B9o8kipnJ6lmnZztCZQX62c50KvAwdX3lL3NX67QU6t%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/1DSZ756xDszA2Vi12h-mAGdjxJACC0uB7Mlau7EdJAZ0/edit?usp=sharing" rel="noopener noreferrer" class="c-link"&gt;
            The Reference Table - 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-2026-v3.ico" width="256" height="256"&gt;
          docs.google.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;"How you prove it" describes how each item is evidenced, not a common certification type. Cost: 🟢 free, 🟠 paid, ⚪ undisclosed.&lt;/p&gt;

&lt;p&gt;The first certification body ANAB-accredited to audit ISO 42001 was Schellman, in September 2024 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;30&lt;/a&gt;. ISO/IEC 42006:2025 followed in July 2025 and set requirements specific to AI-management-system auditors &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;11&lt;/a&gt;. The accredited-auditor pool is barely two years old, and ANAB is one accreditation body among several. Check your certification body's accreditation, and its queue, before you commit to a date.&lt;/p&gt;

&lt;h2&gt;
  
  
  The most expensive misconception
&lt;/h2&gt;

&lt;h3&gt;
  
  
  "We're &lt;a href="https://www.iso.org/standard/42001" rel="noopener noreferrer"&gt;ISO 42001&lt;/a&gt;  certified, so we're EU AI Act compliant."
&lt;/h3&gt;

&lt;p&gt;CSA's April 2026 analysis says it directly: "ISO/IEC 42001 alone does not satisfy the EU AI Act, despite frequent industry shorthand to the contrary." That is CSA's reading, not an EU regulator's formal ruling &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ISO 42001 certification is meaningful evidence that an organization operates an AI management system. It is not, on its own, a finding that every AI system the organization provides or deploys meets the EU AI Act. The Act still wants several things at once, at more than one level: classify the system, document it, manage its risks, govern its data, log it, be transparent, keep human oversight in place, watch it after it ships (post-market monitoring), and, where it applies, run a conformity assessment. On top of that sit organization-level quality-management-system duties under Article 17 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CSA lists the gaps: no per-system regulatory compliance strategy mapping, no pre-determined change management for continuous-learning systems under Article 17(1)(a), no Article 73 serious-incident reporting timetable, generic rather than AI-specific supply-chain provisions, and no structured fundamental-rights consideration, particularly the Article 27 impact assessment certain deployers owe &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Those missing clocks matter. Under Article 73, providers of high-risk systems report immediately after establishing a causal link, or a reasonable likelihood of one, and in any event within 15 days of the provider or, where applicable, the deployer becoming aware. That clock is two days for a widespread infringement or a serious incident involving critical infrastructure as defined in Article 3(49)(b). Where a death is involved, the report is due immediately after establishing or suspecting a causal relationship, and not later than 10 days after becoming aware &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ISO 42001 does not create a presumption of conformity with the Act. Under Article 40, only a harmonised standard cited in the EU Official Journal does that &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;3&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;9&lt;/a&gt;. The candidate is EN 18286  from CEN-CENELEC JTC 21, published as &lt;a href="https://www.cencenelec.eu/news-events/news/2026/en-in-the-spotlight/2026-07-30-ai-quality-management/" rel="noopener noreferrer"&gt;EN 18286:2026&lt;/a&gt;, the first European standard supporting AI Act implementation, covering the Article 17 quality management system for high-risk systems &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;13&lt;/a&gt;. Until a harmonised standard is cited in the Official Journal, a provider audited under ISO 42001 alone will, in CSA's words, "face open questions about whether each of the thirteen Article 17 elements is sufficiently covered, and the answers depend on the auditor's interpretation." &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Other misconceptions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A taxonomy is not a control set.&lt;/strong&gt; OWASP Top 10 is not, by itself, a certification or regulatory compliance regime. Name the risks and mitigations you have addressed; the lists name failure modes, not an auditable program &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;16&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;17&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NIST AI RMF is not certifiable.&lt;/strong&gt; Vendors claim "NIST AI RMF compliance" routinely. No such scheme exists. The July 2025 White House AI Action Plan directed a revision of 1.0, but there is no public draft yet &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;20&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ATLAS is the attacker's view, not a checklist.&lt;/strong&gt; It catalogs adversary techniques. Useful for red teams, wrong as a compliance to-do list &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;18&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COSAiS is not a finished control overlay.&lt;/strong&gt; It is useful for tracking NIST's direction and for federal-aligned planning. There is an August 2025 concept paper and a January 8, 2026 annotated outline covering one of five planned use cases. No finished overlay has been published &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;22&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;23&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the crosswalks actually exist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Official and real:&lt;/strong&gt; CSA ships AICM-to-everything mappings with the v1.1 download: ISO 42001, NIST AI RMF, NIST AI 600-1, EU AI Act, BSI AIC4, and AIUC-1, each with a gap analysis, in five mappings (the two NIST crosswalks ship as one) &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;14&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;15&lt;/a&gt;. OWASP says its LLM Top 10 2026 maps to NIST, MITRE ATLAS, CWE, and its own Agentic Top 10 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;16&lt;/a&gt;. On September 1, 2026, it went further and published a standalone GenAI Security Industry Framework Crosswalk: 51 GenAI vulnerabilities mapped across 25 frameworks, among them ISO 42001, NIST AI RMF, the EU AI Act, MITRE ATLAS, and SOC 2's Trust Services Criteria, free under CC BY-SA 4.0 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;31&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hosted by NIST, authored by a vendor:&lt;/strong&gt; the NIST AI RMF to ISO 42001 crosswalk on NIST's site is credited to Microsoft, not NIST. It's still useful; know what it is &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;24&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stale:&lt;/strong&gt; NIST's own EU mapping sits inside its OECD/EU/EO13960 crosswalk, dated January 26, 2023, and it maps the 2021 proposal, superseded by Regulation (EU) 2024/1689 [24](Stale: NIST's own EU mapping sits inside its OECD/EU/EO13960 crosswalk, dated January 26, 2023, and it maps the 2021 proposal, superseded by Regulation (EU) 2024/1689 &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;24&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not published by AICPA:&lt;/strong&gt; ISO 42001 to SOC 2. AICPA's Trust Services Criteria mapping page lists no AI, ISO 42001, or NIST AI RMF mapping at all, and does not name the other mappings it offers on the public page &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;25&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;26&lt;/a&gt;. There is also no AI-specific SOC 2; teams typically map AI controls into the common criteria and the Processing Integrity category.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4we9xn1wdxecmolpgtxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4we9xn1wdxecmolpgtxu.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CSA AICM maps to six other frameworks in five mappings, each with a gap analysis. The dashed lines are the stale or vendor-authored crosswalks; AICPA publishes no AI mapping for SOC 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;We found no standards body that publishes a "start here" recommendation. Every prescriptive starting-point claim we did find came from a vendor with a product to sell. So treat this as our editorial judgment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8s1ck19vxe38urzf4sgr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8s1ck19vxe38urzf4sgr.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where to start, in order: two free frameworks now, then NIST AI RMF, ISO 42001, EU AI Act Chapter III, COSAiS for horizon scanning, and AIUC-1 only when it applies to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the API layer contributes
&lt;/h2&gt;

&lt;p&gt;These frameworks define the controls and evidence an organization needs. They don't tell engineering how to produce that evidence. That gap is where the API layer matters. A federated graph records which services expose which data, how schemas change, and who approves those changes. Where identity, access controls, and audit logging are configured, it records who changed the graph; client identification labels requests with the client name and version the caller sends &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;27&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wundergraph.com/hub" rel="noopener noreferrer"&gt;Hub&lt;/a&gt; governs the workflow around the graph. Where proposal checks and ownership are enabled, checks run before a proposal ships, and type and subgraph ownership decides who signs off, with graph admins able to approve anything &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;28&lt;/a&gt;. Where configured, Cosmo's graph pruning flags fields deleted without being deprecated first, on a configurable grace period, and a separate operations check fails a deletion while real client traffic still uses the field &lt;a href="https://wundergraph.com/blog/enterprise-ai-governance-frameworks#references" rel="noopener noreferrer"&gt;29&lt;/a&gt;. The result is an approval and change history teams can inspect later.&lt;/p&gt;

&lt;p&gt;Cosmo is not an AI-governance product and should not be positioned as one. It can contribute evidence by governing the APIs, schemas, access paths, change approvals, and client traffic an AI-enabled application uses. Whether that evidence is sufficient depends on the applicable system, role, risk classification, and control requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wundergraph.com/blog/how-cosmo-meets-compliance-requirements" rel="noopener noreferrer"&gt;How Cosmo meets compliance requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wundergraph.com/blog/graphql-federation-subgraph-compliance" rel="noopener noreferrer"&gt;Subgraph compliance in federated graphs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wundergraph.com/blog/ai-non-compliance-costs" rel="noopener noreferrer"&gt;What non-compliance actually costs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Sources verified September 4, 2026; corrections re-verified September 8, 2026.&lt;/p&gt;

&lt;p&gt;[1] Council of the EU. &lt;a href="https://www.consilium.europa.eu/en/press/press-releases/2026/06/29/artificial-intelligence-council-gives-final-green-light-to-simplify-and-streamline-rules/" rel="noopener noreferrer"&gt;"Artificial intelligence: Council gives final green light to simplify and streamline rules."&lt;/a&gt;  Press release, 29 June 2026.&lt;/p&gt;

&lt;p&gt;[2] EUR-Lex. &lt;a href="https://eur-lex.europa.eu/eli/reg/2026/1744/oj" rel="noopener noreferrer"&gt;"Regulation (EU) 2026/1744 of 8 July 2026 (Digital Omnibus on AI), amending Regulation (EU) 2024/1689."&lt;/a&gt;  In force 27 July 2026. Cited for the amended wording of Articles 5, 6, 57, 75 to 75d, 99, 111 and 113, and Annex VIII.&lt;/p&gt;

&lt;p&gt;[3] EUR-Lex. &lt;a href="https://eur-lex.europa.eu/eli/reg/2024/1689/oj" rel="noopener noreferrer"&gt;"Regulation (EU) 2024/1689 (AI Act)."&lt;/a&gt;  Articles 2, 4, 5, 6, 17, 27, 40, 43, 49, 50, 73, 99, 111.&lt;/p&gt;

&lt;p&gt;[4] Cloud Security Alliance. &lt;a href="https://labs.cloudsecurityalliance.org/research/csa-research-note-eu-ai-act-high-risk-deadline-omnibus-20260/" rel="noopener noreferrer"&gt;"EU AI Act's High-Risk Deadline: Deferred, Not Cancelled."&lt;/a&gt;  1 August 2026. Interpretation and practical context, not a substitute for the amending regulation.&lt;/p&gt;

&lt;p&gt;[5] Gibson Dunn. &lt;a href="https://www.gibsondunn.com/eu-ai-act-omnibus-agreement-postponed-high-risk-deadlines-and-other-key-changes/" rel="noopener noreferrer"&gt;"EU AI Act Omnibus Agreement: Postponed High-Risk Deadlines and Other Key Changes."&lt;/a&gt;  27 May 2026, pre-adoption analysis, used for legislative-history context only.&lt;/p&gt;

&lt;p&gt;[6] Cooley. &lt;a href="https://cdp.cooley.com/digital-ai-omnibus-delays-key-deadlines-introduces-new-rules/" rel="noopener noreferrer"&gt;"Digital AI Omnibus Delays Key Deadlines, Introduces New Rules."&lt;/a&gt;  3 August 2026, used for legislative-history context only.&lt;/p&gt;

&lt;p&gt;[7] Anthropic. &lt;a href="https://www.anthropic.com/news/claude-text-watermark" rel="noopener noreferrer"&gt;"How Claude's text watermark works."&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;[8] Anthropic. &lt;a href="https://support.claude.com/en/articles/16266773-how-claude-marks-ai-generated-content" rel="noopener noreferrer"&gt;"How Claude marks AI-generated content."&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;[9] Cloud Security Alliance. &lt;a href="https://labs.cloudsecurityalliance.org/research/csa-research-note-eu-ai-act-pren-18286-iso-42001-20260428-cs/" rel="noopener noreferrer"&gt;"EU AI Act Compliance: prEN 18286 and ISO 42001."&lt;/a&gt;  28 April 2026. CSA's analysis of the ISO 42001 / AI Act relationship, not an EU regulator's ruling.&lt;/p&gt;

&lt;p&gt;[10] ISO/IEC 42001:2023, &lt;a href="https://www.iso.org/standard/42001" rel="noopener noreferrer"&gt;"Information technology. Artificial intelligence. Management system."&lt;/a&gt;  Published December 2023, edition 1, CHF 225.&lt;/p&gt;

&lt;p&gt;[11] ISO/IEC 42006:2025, &lt;a href="https://www.iso.org/standard/42006" rel="noopener noreferrer"&gt;"Requirements for bodies providing audit and certification of artificial intelligence management systems."&lt;/a&gt;  Published July 2025.&lt;/p&gt;

&lt;p&gt;[12] Vanta. &lt;a href="https://www.vanta.com/collection/iso-42001/iso-42001-certification-cost" rel="noopener noreferrer"&gt;"ISO 42001 certification costs: A structural breakdown."&lt;/a&gt;  Vendor estimate, cited as illustrative only.&lt;/p&gt;

&lt;p&gt;[13] CEN-CENELEC. &lt;a href="https://www.cencenelec.eu/news-events/news/2026/en-in-the-spotlight/2026-07-30-ai-quality-management/" rel="noopener noreferrer"&gt;"EN 18286 in the spotlight: supporting compliance with the AI Act."&lt;/a&gt;  31 July 2026.&lt;/p&gt;

&lt;p&gt;[14] Cloud Security Alliance. &lt;a href="https://cloudsecurityalliance.org/blog/2026/07/14/ai-controls-matrix-v1-1-strengthening-the-foundation-for-trustworthy-ai" rel="noopener noreferrer"&gt;"AI Controls Matrix v1.1: Strengthening the Foundation for Trustworthy AI."&lt;/a&gt;  14 July 2026.&lt;/p&gt;

&lt;p&gt;[15] Cloud Security Alliance. &lt;a href="https://cloudsecurityalliance.org/artifacts/ai-controls-matrix-v1-1" rel="noopener noreferrer"&gt;"AI Controls Matrix v1.1" &lt;/a&gt; artifact and mapping bundle.&lt;/p&gt;

&lt;p&gt;[16] OWASP GenAI Security Project. &lt;a href="https://genai.owasp.org/resource/owasp-genai-llm-top-10-2026/" rel="noopener noreferrer"&gt;"OWASP GenAI LLM Top 10 2026."&lt;/a&gt;  Released 3 August 2026, CC BY-SA 4.0.&lt;/p&gt;

&lt;p&gt;[17] OWASP GenAI Security Project. &lt;a href="https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/" rel="noopener noreferrer"&gt;"OWASP Top 10 for Agentic Applications."&lt;/a&gt;  9 December 2025.&lt;/p&gt;

&lt;p&gt;[18] MITRE ATLAS. &lt;a href="https://github.com/mitre-atlas/atlas-data/releases" rel="noopener noreferrer"&gt;atlas-data releases, v2026.05, 27 May 2026 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[19] Cloud Security Alliance. &lt;a href="https://cloudsecurityalliance.org/press-releases/2026/06/30/csa-extends-leadership-into-agentic-ai-with-addition-of-aiuc-1-certification-to-star-registry" rel="noopener noreferrer"&gt;"Cloud Security Alliance Extends AI Assurance Leadership Into Agentic AI With Addition of AIUC-1 Certification to STAR Registry."&lt;/a&gt;  30 June 2026.&lt;/p&gt;

&lt;p&gt;[20] NIST. &lt;a href="https://www.nist.gov/itl/ai-risk-management-framework" rel="noopener noreferrer"&gt;"AI Risk Management Framework (AI RMF 1.0)."&lt;/a&gt;  January 2023. Revision directed by the White House &lt;a href="https://www.whitehouse.gov/wp-content/uploads/2025/07/Americas-AI-Action-Plan.pdf" rel="noopener noreferrer"&gt;"America's AI Action Plan"&lt;/a&gt; , July 2025.&lt;/p&gt;

&lt;p&gt;[21] NIST AI 600-1. &lt;a href="https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.600-1.pdf" rel="noopener noreferrer"&gt;"Artificial Intelligence Risk Management Framework: Generative Artificial Intelligence Profile."&lt;/a&gt;  July 2024.&lt;/p&gt;

&lt;p&gt;[22] NIST CSRC. &lt;a href="https://csrc.nist.gov/Projects/cosais/publications" rel="noopener noreferrer"&gt;"SP 800-53 Control Overlays for Securing AI Systems (COSAiS): publications."&lt;/a&gt;  Annotated outline, 8 January 2026.&lt;/p&gt;

&lt;p&gt;[23] NIST CSRC. &lt;a href="https://csrc.nist.gov/Projects/cosais/use-cases" rel="noopener noreferrer"&gt;"COSAiS use cases."&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;[24] NIST AIRC. &lt;a href="https://airc.nist.gov/AI_RMF_Knowledge_Base/Crosswalks" rel="noopener noreferrer"&gt;"Crosswalks to the NIST AI Risk Management Framework (AI RMF 1.0)."&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;[25] AICPA &amp;amp; CIMA. &lt;a href="https://www.aicpa-cima.com/resources/download/2017-trust-services-criteria-with-revised-points-of-focus-2022" rel="noopener noreferrer"&gt;"2017 Trust Services Criteria (with revised points of focus, 2022)."&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;[26] AICPA &amp;amp; CIMA. &lt;a href="https://www.aicpa-cima.com/resources/article/get-mappings-relevant-to-the-soc-suite-of-services" rel="noopener noreferrer"&gt;"Get mappings relevant to the trust services criteria." &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[27] WunderGraph Cosmo docs. &lt;a href="https://cosmo-docs.wundergraph.com/security-and-compliance" rel="noopener noreferrer"&gt;"Security and compliance"&lt;/a&gt; , &lt;a href="https://cosmo-docs.wundergraph.com/studio/rbac" rel="noopener noreferrer"&gt;"RBAC"&lt;/a&gt; , &lt;a href="https://cosmo-docs.wundergraph.com/studio/groups" rel="noopener noreferrer"&gt;"Groups"&lt;/a&gt; , &lt;a href="https://cosmo-docs.wundergraph.com/studio/sso" rel="noopener noreferrer"&gt;"SSO"&lt;/a&gt; , &lt;a href="https://cosmo-docs.wundergraph.com/studio/audit-log" rel="noopener noreferrer"&gt;"Audit log"&lt;/a&gt; , &lt;a href="https://cosmo-docs.wundergraph.com/studio/changelog" rel="noopener noreferrer"&gt;"Changelog"&lt;/a&gt; , and &lt;a href="https://cosmo-docs.wundergraph.com/studio/analytics/client-identification" rel="noopener noreferrer"&gt;"Client identification"&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;[28] WunderGraph Hub docs. &lt;a href="https://hub-docs.wundergraph.com/guides/propose-and-review" rel="noopener noreferrer"&gt;"Propose and review"&lt;/a&gt;  and &lt;a href="https://hub-docs.wundergraph.com/guides/assign-ownership" rel="noopener noreferrer"&gt;"Assign ownership"&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;[29] WunderGraph Cosmo docs. &lt;a href="https://cosmo-docs.wundergraph.com/studio/graph-pruning" rel="noopener noreferrer"&gt;"Graph pruning"&lt;/a&gt;  — deprecation-before-deletion and grace periods — and &lt;a href="https://cosmo-docs.wundergraph.com/studio/schema-checks" rel="noopener noreferrer"&gt;"Schema checks"&lt;/a&gt;  — the operations check against client traffic, 7-day default window, configurable per namespace.&lt;/p&gt;

&lt;p&gt;[30] Schellman. &lt;a href="https://www.schellman.com/blog/news/schellman-becomes-1st-iso-42001-anab-accredited-certification-body" rel="noopener noreferrer"&gt;"Schellman Becomes First ISO 42001 ANAB Accredited Certification Body."&lt;/a&gt;  24 September 2024.&lt;/p&gt;

&lt;p&gt;[31] OWASP GenAI Security Project. &lt;a href="https://genai.owasp.org/resource/genai-security-industry-framework-crosswalk/" rel="noopener noreferrer"&gt;"GenAI Security Industry Framework Crosswalk."&lt;/a&gt;  Released 1 September 2026, CC BY-SA 4.0.&lt;/p&gt;

&lt;p&gt;[32] Anthropic. &lt;a href="https://claude.com/check-content" rel="noopener noreferrer"&gt;"Check if a file was made with Claude." &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[33] European Commission. &lt;a href="https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai" rel="noopener noreferrer"&gt;"AI Act."&lt;/a&gt;  Shaping Europe's digital future. Last update 3 August 2026.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>governance</category>
      <category>security</category>
      <category>enterprise</category>
    </item>
    <item>
      <title>Understanding the N+1 Problem in GraphQL (and Why It’s Not Just GraphQL)</title>
      <dc:creator>WunderGraph</dc:creator>
      <pubDate>Wed, 29 Jul 2026 23:02:05 +0000</pubDate>
      <link>https://dev.to/wundergraph/understanding-the-n1-problem-in-graphql-and-why-its-not-just-graphql-1dg2</link>
      <guid>https://dev.to/wundergraph/understanding-the-n1-problem-in-graphql-and-why-its-not-just-graphql-1dg2</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;The N+1 problem is a general data-access anti-pattern that can appear in REST, resolver-based GraphQL, and federated GraphQL at different layers of the stack. WunderGraph’s breadth-first loading approach can help batch cross-subgraph work at the router layer, but it does not remove the need for batching inside subgraphs. Even when the router batches entity references, a subgraph can still reintroduce N+1 if its reference resolvers fetch one record at a time. DataLoader is a common resolver-level fix that provides scheduling-window batching plus request-scoped caching, but joins, eager loading, denormalized views, or a single set-based query can be better when the access pattern is predictable. Detection usually means correlating a GraphQL operation with repeated backend calls using database logs, traces, or APM tools. Traces may surface symptoms before they identify the root cause.&lt;/p&gt;

&lt;p&gt;A single API request can quietly fan out into hundreds of backend calls. That is the N+1 problem, and it shows up in REST, monolithic GraphQL, and federated GraphQL alike. The shape is the same in each case. What changes is where the repeated work lands — on the client, at the router, inside a subgraph, or in the database.&lt;/p&gt;

&lt;p&gt;This post expands on Claim 4 from our &lt;a href="https://wundergraph.com/blog/fact-checking-graphql-vs-rest" rel="noopener noreferrer"&gt;GraphQL vs REST fact-check&lt;/a&gt; , which argued that different API styles do not eliminate complexity. They move it to different layers. N+1 is one of the clearest examples: in REST it often appears as many client-side requests, in GraphQL as resolver fan-out inside a single operation, and in federation as work distributed across router and subgraph layers.&lt;/p&gt;

&lt;p&gt;You can read more about this shift in &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity" rel="noopener noreferrer"&gt;Where Does API Complexity Live?&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What the N+1 Problem Is
&lt;/h2&gt;

&lt;p&gt;The N+1 pattern is one query to fetch a list, plus one additional query per item to fetch related data. A single GraphQL operation can trigger one backend call or hundreds, depending on how resolvers are implemented.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The N+1 problem happens when your API fetches a list of items using one query, and then issues an additional query for each item in the list.&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In resolver-based GraphQL servers, execution does not automatically batch sibling field resolvers, so repeated data access can be introduced easily and the cost of each field's fetch isn't coordinated by default. &lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; The same pattern can appear in REST clients, ORMs, and batch APIs whenever related data is loaded one item at a time instead of via set-based operations.&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;3&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  A Concrete N+1 Example in Monolithic GraphQL
&lt;/h2&gt;

&lt;p&gt;A common example is a &lt;code&gt;User&lt;/code&gt; type with a one-to-many &lt;code&gt;posts&lt;/code&gt; field. A naive implementation wires two resolvers:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Query.users&lt;/code&gt; runs &lt;code&gt;SELECT * FROM posts WHERE user_id = ?&lt;/code&gt; once per user (N queries).&lt;/p&gt;

&lt;p&gt;For 50 users, the server issues 51 backend calls.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in code — and how DataLoader can fix it:&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="c1"&gt;// import DataLoader from 'dataloader';&lt;/span&gt;

&lt;span class="c1"&gt;// Naive resolvers (N+1)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// 1 query&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAllByUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// N queries&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Batching with DataLoader (request-scoped)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createPostsLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAllByUserIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 1 batched lookup&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postsByUserId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;postsByUserId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;postsByUserId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;postsByUserId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// createContext runs per request, not once globally&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;postsLoader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createPostsLoader&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolversWithLoader&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postsLoader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a request-scoped DataLoader, those 50 &lt;code&gt;User.posts&lt;/code&gt; resolver calls can be batched into one posts lookup, reducing the backend work from 51 calls to 2: one call for users and one call for posts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The N+1 Problem in GraphQL Federation
&lt;/h2&gt;

&lt;p&gt;N+1 in federated GraphQL is easy to misread as a gateway problem. WunderGraph’s breadth-first loading approach can help batch cross-subgraph work at the router layer, but it does not remove the need for batching inside subgraphs. Even when the router batches entity references, a subgraph can still reintroduce N+1 if its reference resolvers fetch one record at a time, for example when &lt;code&gt;__resolveReference&lt;/code&gt; or nested resolvers fan out per entity.&lt;/p&gt;

&lt;p&gt;Understanding the GraphQL federation N+1 problem requires separating two concerns: &lt;strong&gt;router vs subgraph batching.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Router layer.&lt;/strong&gt; When Cosmo Router resolves a federated query, it may send a batch of entity representations to a subgraph in a single request (the &lt;code&gt;_entities&lt;/code&gt; query). WunderGraph’s breadth-first loading approach is one implementation strategy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subgraph layer.&lt;/strong&gt; Inside the subgraph, &lt;code&gt;__resolveReference&lt;/code&gt; resolves entity references, but whether those references are processed efficiently depends on the subgraph’s implementation. If the resolver performs a datastore lookup per entity representation rather than batching those lookups, the subgraph reintroduces N+1 even though the router sent a single batched request.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://wundergraph.com/blog/microservice-dependencies-graphql-federation-requires-provides" rel="noopener noreferrer"&gt;Cross-subgraph dependencies via &lt;code&gt;@requires&lt;/code&gt;&lt;/a&gt; can increase the amount of coordinated work. When a field in one subgraph depends on data from another, the router fetches the required fields first and passes them to the dependent subgraph as part of the entity representation. Each hop is a chance for subgraph-internal N+1 if reference resolvers aren't batched. A query that crosses multiple subgraphs can still generate many backend calls if each &lt;code&gt;__resolveReference&lt;/code&gt; or nested resolver performs single-row lookups.&lt;/p&gt;

&lt;h3&gt;
  
  
  A federated N+1 example
&lt;/h3&gt;

&lt;p&gt;Here's the pattern in concrete terms. An &lt;code&gt;accounts&lt;/code&gt; subgraph owns &lt;code&gt;User&lt;/code&gt;, and a &lt;code&gt;posts&lt;/code&gt; subgraph owns &lt;code&gt;Post:&lt;/code&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="c"&gt;# accounts subgraph: owns User&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# posts subgraph: owns Post and contributes User.posts&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;Post&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;title&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;author&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="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;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;posts&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;Post&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;The &lt;code&gt;posts&lt;/code&gt; subgraph contributes the &lt;code&gt;posts&lt;/code&gt; field to the &lt;code&gt;User&lt;/code&gt; entity, so it resolves that field even though &lt;code&gt;accounts&lt;/code&gt; owns the rest of &lt;code&gt;User&lt;/code&gt;. When a client queries &lt;code&gt;{ users { posts { title } } }&lt;/code&gt;, the router fetches users from &lt;code&gt;accounts&lt;/code&gt;, then sends a batch of user representations to posts to resolve their posts. That batch arrives at the &lt;code&gt;posts&lt;/code&gt; subgraph as a single &lt;code&gt;_entities&lt;/code&gt; query.&lt;/p&gt;

&lt;p&gt;The problem happens inside &lt;code&gt;posts&lt;/code&gt; in this version:&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="c1"&gt;// posts subgraph: naive User.posts resolver (N+1)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// called once per user in the batch, so N DB calls&lt;/span&gt;
    &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAllByUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// posts subgraph: batched User.posts resolver (fixed)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createPostsLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAllByUserIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 1 batched lookup&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;byUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;byUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;byUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;byUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// in context factory (per request)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolversWithLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postsLoader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router may look efficient because it sends a batched &lt;code&gt;_entities&lt;/code&gt; request, but that does not guarantee efficient work inside the subgraph. The subgraph can still issue N DB calls unless the &lt;code&gt;User.posts&lt;/code&gt; resolver uses a DataLoader or equivalent. The fix is the same as in monolithic GraphQL: batch at the layer where the repeated fetches actually happen, which in this case is inside the subgraph.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the N+1 Problem Actually Hurts
&lt;/h2&gt;

&lt;p&gt;N+1 usually becomes a production problem when lists grow, traffic increases, or many requests hit the same resolver path at once. The extra calls compete for database connections, worker threads, and upstream service capacity.&lt;/p&gt;

&lt;p&gt;In production, the failure mode is usually not just “one request got slower.” The extra calls can amplify tail latency, increase timeout rates, and put pressure on shared infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why N+1 Is Not Unique to GraphQL
&lt;/h2&gt;

&lt;p&gt;N+1 is a data-fetching pattern: a similar “one request for the list, one request per item” shape can show up in plain SQL, ORMs, and REST clients as well as in GraphQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plain SQL
&lt;/h3&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;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id, name FROM users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id, title FROM posts WHERE user_id = ?&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the classic N+1 pattern in relational databases. It runs one query to fetch all users, then one additional query per user to fetch posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  ORM with lazy loading
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Query 1: fetch all users&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Hidden query per user: fetch posts for this one user&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks clean, but a lazily loaded &lt;code&gt;user.posts()&lt;/code&gt; can hide a separate query per user under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  A client calling REST endpoints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Request 1: fetch users&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&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="s2"&gt;/api/users&lt;/span&gt;&lt;span class="dl"&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;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Hidden/accidental N requests: fetch posts once per user&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&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="s2"&gt;`/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/posts`&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;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;res&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;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the N+1 shows up as client-side REST calls instead of backend resolver calls: one request for the collection, then N requests for per-user posts. This is a similar N+1 shape to GraphQL; the difference is whether the fan-out happens on the client, in resolvers, or at another backend layer.&lt;/p&gt;

&lt;p&gt;REST can exhibit the same anti-pattern when a client or backend issues one request for a collection and then one request per item, but that is an implementation pattern rather than a property of REST itself. REST can also hide the same pattern server-side when a gateway or backend endpoint fans out to fetch related records one at a time.&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;4&lt;/a&gt; In typical monolithic GraphQL servers, N+1 is hidden behind a single client request and shows up as backend fan‑out inside the resolver layer. Clients can also create an N+1 pattern by issuing multiple related operations instead of one broader operation, although the more characteristic GraphQL case is resolver-level backend fan-out. This pattern makes it easier to ship accidentally and harder to observe unless you are watching backend query counts, subgraph calls, traces, or APM data.&lt;/p&gt;




&lt;h2&gt;
  
  
  DataLoader Rules That Matter in Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create loaders per request.&lt;/strong&gt; Instantiate loaders inside the request context, not globally. GraphQL.js examples typically create loaders per request, and Netflix DGS documents its data loaders as request-scoped by default.&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replace per-item fetches with &lt;code&gt;.load()&lt;/code&gt;.&lt;/strong&gt; In resolvers that fetch related data by key, call &lt;code&gt;ctx.loader.load(id)&lt;/code&gt; instead of querying the database directly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Issue related loads together.&lt;/strong&gt; DataLoader provides scheduling-window batching plus request-scoped caching: it batches &lt;code&gt;.load(id)&lt;/code&gt; calls issued within the same scheduling window and dispatches them together as a batch.&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;6&lt;/a&gt; Code can use &lt;code&gt;.load()&lt;/code&gt; and still fail to batch if each load is awaited before the next one is issued. Use &lt;code&gt;Promise.all(ids.map((id) =&amp;gt; loader.load(id)))&lt;/code&gt; when loading multiple IDs in the same resolver. Awaiting each load before issuing the next one, including in a for-loop, usually prevents batching.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Return results in key order.&lt;/strong&gt; The batch function receives an array of keys and must return results aligned to the input keys. Fetch by set, group the results, then map them back to the original key order, and choose a missing-value policy that fits your loader, such as null or an error.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Alternatives and Complements to DataLoader
&lt;/h3&gt;

&lt;p&gt;DataLoader is a common resolver-level fix, but joins, eager loading, denormalized views, or a single set-based query can be better when the access pattern is predictable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ORM eager loading.&lt;/strong&gt; Many ORMs can fetch parents and children together using joins, subqueries, or select-in loading. This can replace per-row lazy loads without adding DataLoader to every resolver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set-based queries.&lt;/strong&gt; For stable, read-heavy access patterns, a single set-based SQL query using joins, CTEs, or a denormalized view can be simpler and faster than a chain of DataLoaders. This fixes N+1 at the data-access layer instead of patching it at the resolver layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WunderGraph’s breadth-first loading.&lt;/strong&gt; WunderGraph’s &lt;a href="https://wundergraph.com/blog/dataloader_3_0_breadth_first_data_loading" rel="noopener noreferrer"&gt;breadth-first loading&lt;/a&gt; can help batch cross-subgraph work at the router layer instead of every team wiring loaders by hand, but it does not remove the need for batching inside subgraphs.&lt;/p&gt;

&lt;p&gt;If the data can be fetched efficiently in one set-based query, use the query. If the ORM can eager-load the relationship cleanly, use the ORM.&lt;/p&gt;




&lt;h3&gt;
  
  
  How Cosmo addresses this
&lt;/h3&gt;

&lt;p&gt;Cosmo can address one class of N+1 problem at the router/planner layer by batching execution across subgraph boundaries, but that does not automatically eliminate N+1 inside subgraphs, where resolvers may still issue one datastore call per entity or rely on unbatched data access.&lt;/p&gt;

&lt;p&gt;True N+1 detection still requires request traces, subgraph call counts, or backend query logs, even when router-level batching is in place, and even when planner-level algorithms reduce some categories of fan-out.&lt;/p&gt;

&lt;p&gt;Learn more in our &lt;a href="https://wundergraph.com/blog/dataloader_3_0_breadth_first_data_loading" rel="noopener noreferrer"&gt;breadth-first data loading post&lt;/a&gt; .7&lt;/p&gt;




&lt;h3&gt;
  
  
  Detecting N+1 in Practice
&lt;/h3&gt;

&lt;p&gt;Detecting N+1 is less about spotting GraphQL syntax and more about correlating one operation with the backend work it triggers. The clearest signal is the same backend call repeated many times with different IDs inside a single GraphQL operation.&lt;/p&gt;

&lt;p&gt;Tracing and APM tools can show the same pattern from the request side: one GraphQL operation fans out into many database calls, REST calls, or subgraph requests. In federated graphs, subgraph request counts are especially useful because N+1 may appear at the router boundary, inside a subgraph, or both.&lt;/p&gt;

&lt;p&gt;Specifically: a span pattern that scales linearly with entity count is a strong sign that reference resolution is still happening per entity. If query latency scales roughly linearly with list size, same problem. Router traces may still look fine at a glance, or only show indirect symptoms such as elevated latency or high subgraph span counts, while the exact per-entity fan-out stays hidden inside the subgraph.&lt;/p&gt;

&lt;p&gt;Track tail latency and backend-call counts per operation where your telemetry supports it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Practical Takeaway
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DataLoader is a common resolver-level fix:&lt;/strong&gt; joins, eager loading, denormalized views, or a single set-based query can be better when the access pattern is predictable; WunderGraph’s breadth-first loading can reduce cross-subgraph fan-out as a complement.&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;1&lt;/a&gt; &lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;**Federation can move N+1 to a different layer, so router-level batching and subgraph-level batching both matter. **Even when the router batches entity references, a subgraph can still reintroduce N+1 if its reference resolvers fetch one record at a time.&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem#references" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In resolver-based GraphQL servers, the default execution model makes N+1 easy to introduce, not inevitable.&lt;/strong&gt; Avoiding it requires deliberate choices at the resolver, data-access, or planner layer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the real answer to &lt;a href="https://wundergraph.com/blog/fact-checking-graphql-vs-rest" rel="noopener noreferrer"&gt;Claim 4 in our GraphQL vs REST fact-check:&lt;/a&gt; GraphQL can make N+1 easier to create, but the fix is not “use REST.” The fix is usually to batch or reshape data access at the layer where the repeated work is created, and to be explicit about which layer owns that work, as we argue in the &lt;a href="https://wundergraph.com/blog/rest-vs-graphql-vs-federation-complexity" rel="noopener noreferrer"&gt;API complexity&lt;/a&gt; post.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the WunderGraph publications:&lt;/em&gt;&lt;br&gt;
&lt;a href="https://wundergraph.com/blog/graphql-n-plus-1-problem" rel="noopener noreferrer"&gt;https://wundergraph.com/blog/graphql-n-plus-1-problem&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;[1] Apollo GraphQL. "Handling the N+1 Problem." &lt;a href="https://www.apollographql.com/docs/graphos/schema-design/guides/handling-n-plus-one" rel="noopener noreferrer"&gt;https://www.apollographql.com/docs/graphos/schema-design/guides/handling-n-plus-one&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] GraphQL.js. "Solving the N+1 Problem with DataLoader." &lt;a href="https://www.graphql-js.org/docs/n1-dataloader/" rel="noopener noreferrer"&gt;https://www.graphql-js.org/docs/n1-dataloader/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] REST API Tutorial. "What is N+1 Problem in REST?" &lt;a href="https://restfulapi.net/rest-api-n-1-problem/" rel="noopener noreferrer"&gt;https://restfulapi.net/rest-api-n-1-problem/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[4] Kheyrollahi, A. "Web APIs and the n+1 Problem." InfoQ. &lt;a href="https://www.infoq.com/articles/N-Plus-1/" rel="noopener noreferrer"&gt;https://www.infoq.com/articles/N-Plus-1/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[5] Netflix DGS Framework. "Data Loaders (N+1)." &lt;a href="https://netflix.github.io/dgs/data-loaders/" rel="noopener noreferrer"&gt;https://netflix.github.io/dgs/data-loaders/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[6] GraphQL Foundation. "DataLoader" (reference implementation). &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;[7] WunderGraph. "Dataloader 3.0: A new algorithm to solve the N+1 Problem." &lt;a href="https://wundergraph.com/blog/dataloader_3_0_breadth_first_data_loading" rel="noopener noreferrer"&gt;https://wundergraph.com/blog/dataloader_3_0_breadth_first_data_loading&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>architecture</category>
      <category>performance</category>
    </item>
    <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://dev.to/wundergraph/the-three-bottlenecks-ai-cant-code-away-5cei</link>
      <guid>https://dev.to/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://dev.to/wundergraph/where-does-api-complexity-live-31ik</link>
      <guid>https://dev.to/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://dev.to/wundergraph/graphql-vs-rest-18-claims-fact-checked-with-primary-sources-2026-117l</link>
      <guid>https://dev.to/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://dev.to/wundergraph/how-the-fission-algorithm-works-top-down-graphql-federation-design-507n</link>
      <guid>https://dev.to/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>
