<?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: Stefan  🚀</title>
    <description>The latest articles on DEV Community by Stefan  🚀 (@slickstef11).</description>
    <link>https://dev.to/slickstef11</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F702459%2Fb118d2d9-d2ea-4b0f-81ad-977681778d54.JPG</url>
      <title>DEV Community: Stefan  🚀</title>
      <link>https://dev.to/slickstef11</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/slickstef11"/>
    <language>en</language>
    <item>
      <title>Catching Hidden Compliance Risks in GraphQL Federation with @openfed__requireFetchReasons</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Sun, 15 Mar 2026 19:30:25 +0000</pubDate>
      <link>https://dev.to/slickstef11/catching-hidden-compliance-risks-in-graphql-federation-with-openfedrequirefetchreasons-5aco</link>
      <guid>https://dev.to/slickstef11/catching-hidden-compliance-risks-in-graphql-federation-with-openfedrequirefetchreasons-5aco</guid>
      <description>&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;@requires isn’t just a helper directive. In practice, it can leak sensitive fields across subgraphs and create compliance headaches. The @openfed__requireFetchReasons directive fixes this by making fetch reasons explicit and giving subgraphs control over who can access sensitive fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Problem with @requires
&lt;/h2&gt;

&lt;p&gt;Most of us use @requires without a second thought. It’s convenient, and it works. But in a federated setup, it can quietly bypass the data boundaries you think are in place.&lt;/p&gt;

&lt;p&gt;From a compliance angle, that’s dangerous. The audit team might assume a field is locked down, but another subgraph could still access it by tacking on a @requires. That’s how sensitive data ends up in places it shouldn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: An Auction Service with a Leak
&lt;/h2&gt;

&lt;p&gt;Here’s a stripped-down case. A fictional company, LeeWay, lets people auction off their old software. Two subgraphs handle the logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auction subgraph&lt;/strong&gt; defines the auction and its sensitive minimumPrice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description subgraph&lt;/strong&gt; extends the type with a description field that uses @requires.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# Auction Subgraph&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;auction&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;Auction&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;Auction&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;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;minimumPrice&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="c"&gt;# Description Subgraph with exploitable directives.&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;Auction&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;minimumPrice&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="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;external&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;description&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="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;requires&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;minimumPrice&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if a client asks for description, the Router will fetch minimumPrice from the Auction subgraph and pass it along:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"query($representations: [_Any!]!) { _entities(representations: $representations) { ... on Auction { description } } }"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variables"&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="nl"&gt;"representations"&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="nl"&gt;"__typename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Auction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&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="nl"&gt;"minimumPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&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;Suddenly, minimumPrice is exposed. Authorization checks still exist, but compliance controls are broken because another subgraph now has access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing It with @openfed__requireFetchReasons
&lt;/h2&gt;

&lt;p&gt;WunderGraph’s @openfed__requireFetchReasons directive locks this down. You apply it to the sensitive field:&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;# Auction Subgraph&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;auction&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;Auction&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;Auction&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;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;minimumPrice&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="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;openfed__requireFetchReasons&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;With the directive, the Router includes fetch reasons in the extensions field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&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="nl"&gt;"fetch_reasons"&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="nl"&gt;"typename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Auction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minimumPrice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"by_subgraphs"&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="s2"&gt;"Description"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"is_requires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;The Auction subgraph can then parse this and decide: is the Description subgraph allowed to fetch this field? If not, deny it.&lt;/p&gt;

&lt;p&gt;To cover direct user access, the Router adds a by_user flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&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="nl"&gt;"fetch_reasons"&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="nl"&gt;"typename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Auction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minimumPrice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"by_user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;That way, you can distinguish between a legitimate client request and a dependency from another subgraph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compliance and Audit Benefits
&lt;/h2&gt;

&lt;p&gt;This isn’t just about passing audits. The directive makes sensitive data access explicit, declarative, and auditable. Without safeguards, @requires can expose sensitive fields to other subgraphs.&lt;/p&gt;

&lt;p&gt;With @openfed__requireFetchReasons, you don’t have to guess. The schema itself carries the compliance rules, and the runtime enforces them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-Up
&lt;/h2&gt;

&lt;p&gt;If you’re working with GraphQL Federation and care about data boundaries, don’t ignore @requires. It can punch holes in your compliance story. The @openfed__requireFetchReasons directive gives you a clean, declarative way to control fetch reasons and build trust into your schema.&lt;/p&gt;

&lt;p&gt;Originally posted on &lt;a href="https://wundergraph.com/blog/graphql-federation-subgraph-compliance" rel="noopener noreferrer"&gt;WunderGraph’s blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>microservices</category>
      <category>apiarchitecture</category>
      <category>backend</category>
    </item>
    <item>
      <title>Entities and Batching Cosmo Connect vs Apollo Federation vs GraphQL Federation</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Sun, 15 Mar 2026 16:13:24 +0000</pubDate>
      <link>https://dev.to/slickstef11/entities-and-batching-cosmo-connect-vs-apollo-federation-vs-graphql-federation-dje</link>
      <guid>https://dev.to/slickstef11/entities-and-batching-cosmo-connect-vs-apollo-federation-vs-graphql-federation-dje</guid>
      <description>&lt;p&gt;Federation gives teams a way to build one API layer from many services. The three main options—&lt;a href="https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/federation" rel="noopener noreferrer"&gt;Apollo Federation&lt;/a&gt;, &lt;a href="https://graphql.org/blog/2024-05-16-composite-schemas-announcement" rel="noopener noreferrer"&gt;GraphQL Federation (Composite Schemas)&lt;/a&gt;, and &lt;a href="https://cosmo-docs.wundergraph.com/connect/overview" rel="noopener noreferrer"&gt;Cosmo Connect&lt;/a&gt;—share this goal but solve the problem differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apollo Federation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Uses GraphQL subgraphs with &lt;a class="mentioned-user" href="https://dev.to/key"&gt;@key&lt;/a&gt; to define entities.&lt;/li&gt;
&lt;li&gt;The Router batches lookups through the _entities field.&lt;/li&gt;
&lt;li&gt;Strong composition checks ensure subgraphs join cleanly.&lt;/li&gt;
&lt;li&gt;Drawback: every subgraph must be a GraphQL server. Connectors exist for REST, but they can leak backend details into the API contract.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GraphQL Federation (Composite Schemas)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Driven by the GraphQL Foundation’s Composite Schemas spec.&lt;/li&gt;
&lt;li&gt;Keeps GraphQL “vanilla” by using @lookup directives instead of _entities.&lt;/li&gt;
&lt;li&gt;Pros: federation without Apollo-specific extensions.&lt;/li&gt;
&lt;li&gt;Cons: entity status is less explicit, and batching is not built in. Routers must alias or send multiple requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cosmo Connect
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keeps explicit &lt;a class="mentioned-user" href="https://dev.to/key"&gt;@key&lt;/a&gt; directives, but replaces GraphQL subgraphs with gRPC services.&lt;/li&gt;
&lt;li&gt;You define SDL, generate a proto, and implement RPCs.&lt;/li&gt;
&lt;li&gt;Two integration models:&lt;/li&gt;
&lt;li&gt;Router Plugins (managed processes inside the Router).&lt;/li&gt;
&lt;li&gt;gRPC Services (independent services with full scaling and ownership).&lt;/li&gt;
&lt;li&gt;The Router handles batching, retries, auth, and rate limits.&lt;/li&gt;
&lt;li&gt;gRPC contracts are strict and batch-friendly by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Batching Compared
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apollo Federation:&lt;/strong&gt; _entities gives built-in batching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL Federation:&lt;/strong&gt; @lookup requires manual batching strategies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cosmo Connect:&lt;/strong&gt; gRPC RPCs accept lists of keys, batching is automatic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;All three approaches aim to build a unified API layer with entities. Apollo Federation relies on _entities, GraphQL Federation uses @lookup, and Cosmo Connect compiles SDL into strict gRPC contracts. The tradeoffs lie in how entities are defined and how batching is handled.&lt;/p&gt;

&lt;p&gt;Developers often ask the same practical questions when starting with Connect. Here are the most common ones:&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What’s the difference between Apollo Federation and GraphQL Federation?&lt;/strong&gt; Apollo Federation adds _entities, _Any, and &lt;a class="mentioned-user" href="https://dev.to/key"&gt;@key&lt;/a&gt;. GraphQL Federation avoids these and uses @lookup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When should I use Cosmo Connect instead of GraphQL subgraphs?&lt;/strong&gt; If you want federation without GraphQL servers. Connect compiles SDL to gRPC contracts, and the Router manages platform concerns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Cosmo Connect work with REST APIs and SDKs?&lt;/strong&gt; Yes. Implement RPCs that call REST or SDKs. The Router provides retries, rate limiting, and auth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does batching differ across the three?&lt;/strong&gt; Apollo Federation batches with _entities. GraphQL Federation needs router-side batching. Cosmo Connect includes batch-capable gRPC methods out of the box.&lt;/p&gt;

&lt;p&gt;This post is a condensed summary. The full blog with code examples and history is published on the &lt;a href="https://wundergraph.com/blog/cosmo-connect-vs-apollo-federation-vs-graphql-federation" rel="noopener noreferrer"&gt;WunderGraph blog.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>microservices</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Why Schema Changes Take Months in GraphQL Federation (And What the Fix Looks Like)</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Wed, 11 Mar 2026 20:18:01 +0000</pubDate>
      <link>https://dev.to/slickstef11/why-schema-changes-take-months-in-graphql-federation-and-what-the-fix-looks-like-2f1j</link>
      <guid>https://dev.to/slickstef11/why-schema-changes-take-months-in-graphql-federation-and-what-the-fix-looks-like-2f1j</guid>
      <description>&lt;p&gt;This is a summary of a longer piece published on the WunderGraph blog. &lt;a href="https://wundergraph.com/blog/graphql-federation-was-built-backwards" rel="noopener noreferrer"&gt;Read the full article here.&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you've worked on a large federated graph, you've probably lived this: a frontend team needs a new field. The resolver takes an afternoon to write. Getting approval to write it takes three weeks.&lt;/p&gt;

&lt;p&gt;The reason isn't technical. Federation's composition model is sound — subgraphs give teams independent ownership, the composition layer produces a unified graph, and consumers get one place to query. The problem is everything that has to happen before implementation can start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The coordination problem, specifically
&lt;/h2&gt;

&lt;p&gt;Say you need a shippingEligibility field on User, combining data from a fulfillment service and a compliance service. Before anyone writes a resolver, someone has to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which subgraph owns User? Which team owns that subgraph?&lt;/li&gt;
&lt;li&gt;Can that team extend User with a type they don't own the data for?&lt;/li&gt;
&lt;li&gt;What does the fulfillment team need to expose, and in what shape?&lt;/li&gt;
&lt;li&gt;Does the compliance team's data have a natural entity key that maps cleanly to User?&lt;/li&gt;
&lt;li&gt;Who approves the field name, the type structure, the nullability decisions?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are hard questions in isolation. But in an organization running 50 or 100 subgraphs, with ownership distributed across many teams, the process of answering them — Slack threads, design docs, platform team review, scheduling across backlogs — is where time goes.&lt;/p&gt;

&lt;p&gt;The implementation was never the bottleneck. Alignment was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Federation doesn't solve this
&lt;/h2&gt;

&lt;p&gt;Federation was designed to solve a different problem: how do you scale a GraphQL schema technically across many teams? The answer — distributed subgraph ownership with a composition layer — is a good one for that problem.&lt;/p&gt;

&lt;p&gt;But Federation composes bottom-up. Each team builds their subgraph independently. The supergraph is whatever the composition process produces. There's no design artifact that represents what the consumer-facing API should look like before teams start building. The supergraph emerges; it isn't specified.&lt;/p&gt;

&lt;p&gt;This means there's no natural place for a frontend team to say "here is what we need" and have that statement drive the work. Instead, they have to reverse-engineer who owns what and negotiate the shape of the API across team boundaries — every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a top-down model looks like
&lt;/h2&gt;

&lt;p&gt;WunderGraph's post argues that the workflow should run in reverse. Start with the consumer's ideal query — what they would ask for if there were no constraints. Design the supergraph to answer it. Then figure out which subgraphs need to change and what each team is responsible for.&lt;/p&gt;

&lt;p&gt;In their Fission model, a schema change begins as a supergraph-level proposal. A new type or field is sketched in terms of the consumer contract, before any subgraph assignment happens. The &lt;a href="https://wundergraph.com/blog/graphql-federation-was-built-backwards" rel="noopener noreferrer"&gt;full article walks through the shipping eligibility example step-by-step&lt;/a&gt;, including how entity keys get propagated across subgraphs. Tooling then identifies which subgraphs are affected, propagates entity key requirements automatically, and routes the proposal to the right owners for review. Each team approves their slice. Implementation begins with a validated, agreed-upon spec.&lt;/p&gt;

&lt;p&gt;The result is that the coordination which currently happens informally — across Slack, Miro boards, and meetings — gets a structured surface. Affected teams are identified automatically rather than through institutional knowledge. Sign-off is tracked rather than assumed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for teams running large supergraphs
&lt;/h2&gt;

&lt;p&gt;The value of this model scales with graph size. At five subgraphs with one team, informal coordination is fine. At fifty subgraphs with fifteen teams, the absence of a structured design-time workflow is itself a product risk. Changes move slowly not because engineers are slow, but because the process of getting alignment has no tooling support.&lt;/p&gt;

&lt;p&gt;Fission is positioned as a design-time complement to Federation's runtime machinery — not a replacement. Query planning and execution stay with Federation. The workflow for deciding what the supergraph should look like, and getting teams aligned on it before implementation begins, is what Fission addresses.&lt;/p&gt;

&lt;p&gt;It's also, the post notes, the model that makes most sense as AI agents start contributing to API development. An agent that can query the supergraph for existing capabilities and propose structured changes for missing ones — with a human reviewing before anything ships — fits naturally into this workflow. The coordination layer has to exist for that to work at all.&lt;/p&gt;




&lt;p&gt;The full article walks through an example of a schema change. &lt;a href="https://wundergraph.com/blog/graphql-federation-was-built-backwards" rel="noopener noreferrer"&gt;Read it on the WunderGraph blog.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>graphql</category>
      <category>distributedsystems</category>
      <category>apiarchitecture</category>
    </item>
    <item>
      <title>Running Federation with Cosmo: No Glue Code, No Surprises</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Mon, 09 Mar 2026 18:57:45 +0000</pubDate>
      <link>https://dev.to/slickstef11/running-federation-with-cosmo-no-glue-code-no-surprises-3nkl</link>
      <guid>https://dev.to/slickstef11/running-federation-with-cosmo-no-glue-code-no-surprises-3nkl</guid>
      <description>&lt;p&gt;If you caught &lt;a href="https://wundergraph.com/blog/food-truck-guide-to-graphql-federation" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, we walked through Federation with a food park analogy. This time, we're digging into what Cosmo actually does.&lt;/p&gt;

&lt;p&gt;It’s not magic. Cosmo validates, routes, and coordinates your GraphQL subgraphs using the schema itself, so you get fewer bugs, fewer surprises, and no glue code to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Schema Composition: Catching Issues Before They Go Live
&lt;/h2&gt;

&lt;p&gt;Every morning (or CI run), each team submits their subgraph schema to Cosmo. Cosmo composes them into a supergraph and checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate type or field names&lt;/li&gt;
&lt;li&gt;Ownership conflicts&lt;/li&gt;
&lt;li&gt;Missing types or fields required by another subgraph&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If anything’s off, composition fails and sends the schema back for review.&lt;/p&gt;

&lt;p&gt;You’ll see exactly which service caused the issue and why—before it breaks your API.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Query Planning: Routing Based on Ownership
&lt;/h2&gt;

&lt;p&gt;Once everything’s composed, Cosmo uses the supergraph to plan how to resolve queries. Each field is mapped to the subgraph that owns it.&lt;/p&gt;

&lt;p&gt;Let’s say a client sends:&lt;/p&gt;

&lt;p&gt;graphql&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;OrderSummary&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;sides&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;drinks&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;type&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;Cosmo builds a query plan that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Send `sides` query to Fry Truck subgraph  
2. Send `drinks` query to Smoothie Truck subgraph  
3. Stitch together response and return to client  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of it is driven by schema metadata—no hand-coded routing rules, no guesswork.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ When Things Break: Cosmo Handles Fallback
&lt;/h2&gt;

&lt;p&gt;Stuff goes wrong. Services go down. Fields error out. Cosmo doesn’t crash the whole response.&lt;/p&gt;

&lt;p&gt;Here’s what it does instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follows fallback rules if they’re defined&lt;/li&gt;
&lt;li&gt;Retries if configured&lt;/li&gt;
&lt;li&gt;Returns partial data and suppresses internal errors when needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fallback behavior is defined in the routing layer, not guessed at runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  📡 Real-Time Subscriptions: Subgraphs + Pub/Sub + Cosmo
&lt;/h2&gt;

&lt;p&gt;Here’s the catch with subscriptions: they’re hard to federate.&lt;/p&gt;

&lt;p&gt;Cosmo solves this with EDFS—Event-Driven Federated Subscriptions. Each subgraph sends events to a pub/sub system like NATS or Kafka. Cosmo listens to those streams, maps the events to the right GraphQL subscription using the composed schema and routing metadata, and filters out anything the client didn’t ask for.&lt;/p&gt;

&lt;p&gt;No polling. No global fan-out. No manual wiring. Cosmo handles everything through the same schema-aware routing layer you’re already using for queries.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧰 You Focus on Features. Cosmo Runs the Federation.
&lt;/h2&gt;

&lt;p&gt;You don’t have to build orchestration logic from scratch. Cosmo takes care of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema composition&lt;/li&gt;
&lt;li&gt;Query planning&lt;/li&gt;
&lt;li&gt;Runtime routing&lt;/li&gt;
&lt;li&gt;Error masking and fallback&lt;/li&gt;
&lt;li&gt;Federated subscriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It connects with your GitHub repo, deploys via the CLI, and lets you track everything in Studio.&lt;/p&gt;

&lt;p&gt;Cosmo doesn’t just move data. It’s how your federated platform stays reliable at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next up:&lt;/strong&gt; Schema contracts and how Cosmo enforces them before things break.&lt;/p&gt;

&lt;p&gt;Read the original: &lt;a href="https://wundergraph.com/blog/behind-the-counter-how-cosmo-works" rel="noopener noreferrer"&gt;wundergraph.com/blog/behind-the-counter-how-cosmo-works&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>distributedsystems</category>
      <category>api</category>
      <category>apiarchitecture</category>
    </item>
    <item>
      <title>Event Lifecycle Control in GraphQL Subscriptions</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Sun, 08 Mar 2026 13:38:46 +0000</pubDate>
      <link>https://dev.to/slickstef11/event-lifecycle-control-in-graphql-subscriptions-l14</link>
      <guid>https://dev.to/slickstef11/event-lifecycle-control-in-graphql-subscriptions-l14</guid>
      <description>&lt;p&gt;Cosmo Streams adds three handlers to the router that control subscription behavior: authorization when a client subscribes, per-subscriber filtering while events flow, and validation when mutations publish events. Instead of building custom subscription infrastructure, you write Go functions that run at specific points in the event lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What EDFS Already Solved
&lt;/h2&gt;

&lt;p&gt;EDFS connects your message broker to GraphQL subscriptions. The router listens for events and publishes them as subscriptions. This remains the foundation of how Cosmo Streams operates.&lt;/p&gt;

&lt;p&gt;Cosmo Streams extends that by moving subscription authorization, event filtering, and mutation validation into the router itself. Three handlers cover three stages: when a subscription starts, while events are in flight, and when mutations publish events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorization Was All or Nothing
&lt;/h2&gt;

&lt;p&gt;EDFS gave you binary access: authenticated users could subscribe to any topic. There was no way to scope access based on roles, user attributes, or subscription context.&lt;/p&gt;

&lt;p&gt;Teams worked around this by building custom services, adding resolver checks, or processing events after delivery. That meant managing connection state, duplicating auth logic, and manually fanning out events.&lt;/p&gt;

&lt;h3&gt;
  
  
  SubscriptionOnStart Handler
&lt;/h3&gt;

&lt;p&gt;The SubscriptionOnStart handler runs when a client subscribes. You have access to JWT data, HTTP headers, connection details, and GraphQL variables. Use this context to allow or deny the subscription.&lt;/p&gt;

&lt;p&gt;Example: users subscribe to order updates. The handler validates the JWT and issuer before allowing the subscription. Once established, the OnReceiveEvent handler filters which events each subscriber receives based on permissions.&lt;/p&gt;

&lt;p&gt;Two users subscribe to the same GraphQL subscription but see different events. Per-subscriber filtering wasn't possible with EDFS alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Subscribers Started from Zero
&lt;/h2&gt;

&lt;p&gt;EDFS is purely event-driven. Subscribe, then receive future events as they happen. Efficient, but creates UX issues.&lt;/p&gt;

&lt;p&gt;A user joins a live soccer match stream. Score is already 1-0. With EDFS, they see nothing until the next goal, which might be 30 minutes away. The client is connected but has no context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial State via SubscriptionOnStart
&lt;/h3&gt;

&lt;p&gt;The same handler that handles authorization can send data immediately when a subscription starts.&lt;/p&gt;

&lt;p&gt;A client subscribes to an in-progress match. The router queries the current score, sends it immediately, and then continues streaming future goals. The client gets the initial state, then the event stream.&lt;/p&gt;

&lt;p&gt;One handler, two problems: access control and initial data delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everyone Got Identical Events
&lt;/h2&gt;

&lt;p&gt;EDFS fanned out events to all subscribers identically. Simple, performant, but inflexible. Some teams needed to filter or modify events per subscriber.&lt;/p&gt;

&lt;p&gt;Two users subscribe to the same topic. One's an admin who should see everything. The other's a regular user who should only see their own data.&lt;/p&gt;

&lt;h3&gt;
  
  
  OnReceiveEvent Handler
&lt;/h3&gt;

&lt;p&gt;OnReceiveEvent runs when an event arrives from the broker, before the router delivers it. Executes once per subscriber, enabling per-subscriber decisions about delivery.&lt;/p&gt;

&lt;p&gt;Example: user subscribes to order updates, receives only events from orders they created. Handler filters based on customer ID from the token matched against customer ID in the event.&lt;/p&gt;

&lt;p&gt;This filtering happens for each subscriber. 30,000 subscribers means 30,000 handler executions. Router handles these asynchronously with configurable concurrency limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutation Side Validation
&lt;/h2&gt;

&lt;p&gt;OnPublishEvent runs when a mutation publishes an event through Cosmo Streams, before the router emits to the message system. Validates or enriches data before it enters your event infrastructure.&lt;/p&gt;

&lt;p&gt;Example: user creates an order via mutation. Handler validates the user is creating an order for themselves, not another user. Matches customer ID from token against customer ID in mutation input.&lt;/p&gt;

&lt;p&gt;Without this, router-level validation was difficult. EDFS just transformed mutation data into events and sent them out. Now you have a validation layer before events enter your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Handlers are implemented through the &lt;a href="https://cosmo-docs.wundergraph.com/router/cosmo-streams/custom-modules" rel="noopener noreferrer"&gt;Custom Module&lt;/a&gt; system. Write logic in Go as modules that compile with the router.&lt;/p&gt;

&lt;p&gt;Three extension points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SubscriptionOnStart - when subscriptions start&lt;/li&gt;
&lt;li&gt;OnReceiveEvent - when events arrive from broker&lt;/li&gt;
&lt;li&gt;OnPublishEvent - when mutations publish events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All handlers are optional. Without them, router falls back to standard EDFS behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;OnReceiveEvent processes every subscriber individually. 30,000 subscribers = 30,000 handler runs per event batch.&lt;/p&gt;

&lt;p&gt;Don't make API calls per handler run. That's 30K external calls per event with 30K subscribers. Use in-process or external caches. Custom Modules are Go functions, so you can run async routines and look up pre-computed data instead of hitting upstream systems.&lt;/p&gt;

&lt;p&gt;Handler logic must be efficient. The router handles concurrency and memory management, but blocking operations at this scale will cause problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/wundergraph/cosmo-streams-demo" rel="noopener noreferrer"&gt;demo repository&lt;/a&gt; shows all three handlers with a working order system. See how SubscriptionOnStart validates JWTs, how OnPublishEvent ensures users only create their own orders, and how OnReceiveEvent filters events so customers see only their own orders.&lt;/p&gt;

&lt;p&gt;Cosmo Streams turns GraphQL subscriptions from a broadcast pipe into a policy-aware event system with control at subscribe time, in-flight, and at publish time.&lt;/p&gt;




&lt;p&gt;Adapted from the original article on the &lt;a href="https://wundergraph.com/blog/3-handlers-and-their-solutions-streams" rel="noopener noreferrer"&gt;WunderGraph blog.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>distributedsystems</category>
      <category>apiarchitecture</category>
    </item>
    <item>
      <title>Extending Your GraphQL Router with TypeScript</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Sun, 08 Mar 2026 05:19:25 +0000</pubDate>
      <link>https://dev.to/slickstef11/extending-your-graphql-router-with-typescript-356f</link>
      <guid>https://dev.to/slickstef11/extending-your-graphql-router-with-typescript-356f</guid>
      <description>&lt;p&gt;Cosmo Connect now supports TypeScript plugins. If you're already working in a TypeScript codebase, you can extend your GraphQL router without introducing Go or deploying additional services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Until now, router plugins required Go. That works fine for teams already using it, but creates friction for everyone else. Since most teams already write TypeScript, adding native support removes that barrier.&lt;/p&gt;

&lt;p&gt;The typical use case: you need to expose some internal logic or wrap a legacy REST API without spinning up another service. Write a TypeScript plugin, define your GraphQL type, call your API, and transform the response. It functions like a subgraph but lives inside the router.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Setup
&lt;/h2&gt;

&lt;p&gt;Cosmo Connect generates protobuf definitions from your GraphQL schema. When you write a plugin, you define the schema, generate the protos, implement handlers, and publish. The router handles the rest.&lt;/p&gt;

&lt;p&gt;Scaffold a new plugin:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wgc router plugin init planets --language ts

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate protobuf definitions:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wgc router plugin generate --plugin-dir ./planets

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your schema might look like:&lt;/p&gt;

&lt;p&gt;graphql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {
  planet: Planet!
}

type Planet {
  name: String!
  mass: Float!
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every schema change requires regenerating protos so your plugin code has access to new fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Handler logic goes in src/plugin-server.ts. Here's a simple example:&lt;/p&gt;

&lt;p&gt;typescript&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async QueryPlanet(
  call: ServerUnaryCall&amp;lt;QueryPlanetRequest, QueryPlanetResponse&amp;gt;,
  callback: sendUnaryData&amp;lt;QueryPlanetResponse&amp;gt;
): Promise&amp;lt;void&amp;gt; {
  const response = new QueryPlanetResponse();
  const planet = new Planet();
  planet.setName("Earth");
  planet.setMass(5.972e24);
  response.setPlanet(planet);
  callback(null, response);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, replace this with calls to your internal services, databases, or legacy systems.&lt;/p&gt;

&lt;p&gt;Build and publish:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wgc router plugin build --plugin-dir ./planets
wgc router plugin publish planets --namespace &amp;lt;namespace&amp;gt;
wgc subgraph update planets --namespace &amp;lt;namespace&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router reloads automatically, and your new field appears in the schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema Validation
&lt;/h2&gt;

&lt;p&gt;Published plugins go through standard composition rules. No special checks for TypeScript. Test in the Playground like any other field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go vs TypeScript
&lt;/h2&gt;

&lt;p&gt;Go plugins shipped first because the router is written in Go and uses HashiCorp's go-plugin library - most mechanics are handled out of the box. TypeScript required building more integration ourselves, which took longer.&lt;/p&gt;

&lt;p&gt;If you want deeper context on our plugin architecture decisions, Jens wrote about it in &lt;a href="https://wundergraph.com/blog/generative-api-orchestration" rel="noopener noreferrer"&gt;REST in Peace—Connectors Were Never the Right Supergraph Abstraction.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Best way to learn: wrap a single REST endpoint. Define the type, implement the handler, publish, and query. You'll understand the full workflow in a real environment.&lt;/p&gt;

&lt;p&gt;Complete walkthrough in our &lt;a href="https://cosmo-docs.wundergraph.com/tutorial/using-grpc-plugins" rel="noopener noreferrer"&gt;plugin tutorial.&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Adapted from the original article on the &lt;a href="https://wundergraph.com/blog/typescript-plugin-support-for-connect" rel="noopener noreferrer"&gt;WunderGraph blog.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>typescript</category>
      <category>api</category>
      <category>backenddevelopment</category>
    </item>
    <item>
      <title>AI Accelerates Implementation. It Can't Fix Your Federation Coordination Problem.</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Thu, 05 Mar 2026 20:03:47 +0000</pubDate>
      <link>https://dev.to/slickstef11/ai-accelerates-implementation-it-cant-fix-your-federation-coordination-problem-3kc8</link>
      <guid>https://dev.to/slickstef11/ai-accelerates-implementation-it-cant-fix-your-federation-coordination-problem-3kc8</guid>
      <description>&lt;p&gt;This is a summary of a longer piece on the WunderGraph blog. &lt;a href="https://wundergraph.com/blog/missing-layer-in-graphql-federation" rel="noopener noreferrer"&gt;Read the full article here.&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;There's a common assumption in platform engineering right now: AI will speed everything up. LLMs write code faster. Implementation cycles shrink. Teams ship more.&lt;/p&gt;

&lt;p&gt;That's true for the parts of the job that are already well-defined.&lt;/p&gt;

&lt;p&gt;The bottleneck in GraphQL Federation development isn't implementation. It's alignment. And alignment is exactly what LLMs can't help with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Time Actually Goes
&lt;/h2&gt;

&lt;p&gt;If you're running Federation at scale, you are probably familiar with this pattern. A frontend engineer needs a new field. The implementation is straightforward — maybe a few hours of work. But before a line of code gets written, someone has to answer a set of organizational questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which subgraph owns the type where this field belongs?&lt;/li&gt;
&lt;li&gt;Is that team in a position to add it right now?&lt;/li&gt;
&lt;li&gt;Does the proposed field name conflict with conventions in other subgraphs?&lt;/li&gt;
&lt;li&gt;Who needs to approve this before it's safe to compose?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are technical problems. They're coordination problems. And in organizations running dozens or hundreds of subgraphs, they're expensive ones. Complex schema changes can take months, not because the code is hard, but because getting the right people aligned is.&lt;/p&gt;

&lt;p&gt;The current toolset for this coordination: Slack threads, Miro boards, meetings, and platform engineers acting as human routers for every schema change request. It works until it doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  LLMs Make This Worse Before They Make It Better
&lt;/h2&gt;

&lt;p&gt;Here's the dynamic to pay attention to: as LLMs reduce implementation time, alignment becomes a larger fraction of total cycle time. If implementation drops from a week to a day, but coordination still takes three weeks, you haven't meaningfully accelerated anything.&lt;/p&gt;

&lt;p&gt;The velocity problem in Federation isn't that engineers write code too slowly. It's that getting to "here's exactly what to build" — the right field name, the right type, the right subgraph, with sign-off from the right teams — is where time disappears.&lt;/p&gt;

&lt;p&gt;LLMs accelerate the last mile. They don't touch the first three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Structural Issue Underneath
&lt;/h2&gt;

&lt;p&gt;There's an architectural reason coordination is hard in Federation, and it's worth naming.&lt;/p&gt;

&lt;p&gt;Federation composes the supergraph bottom-up: subgraph teams build independently, and the supergraph is whatever the composition layer produces. The consumer-facing API is a side effect of what teams happened to build.&lt;/p&gt;

&lt;p&gt;GraphQL's original design principle was the opposite: the schema is a contract shaped around what consumers need. Federation, as commonly practiced, inverts this.&lt;/p&gt;

&lt;p&gt;WunderGraph's response to this is an approach called Fission — designing the consumer-facing supergraph first, then decomposing that design into work for subgraph teams. Proposals start at the supergraph level. Affected subgraphs and their owners are identified automatically. Each team reviews and approves their portion before implementation begins.&lt;/p&gt;

&lt;p&gt;This makes the coordination explicit and trackable rather than ad hoc.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Agents Actually Need
&lt;/h2&gt;

&lt;p&gt;The coordination problem gets more acute when you extend it to AI agents.&lt;/p&gt;

&lt;p&gt;An agent working on behalf of a user needs to know what capabilities exist in the graph. If a capability &lt;a href="https://blog.cloudflare.com/code-mode-mcp/" rel="noopener noreferrer"&gt;is missing, it needs a way to request it. As &lt;/a&gt;Cloudflare has noted, it doesn't work to expose thousands of API endpoints to an agent; you need a structured, searchable graph.&lt;/p&gt;

&lt;p&gt;Hub is designed with this trajectory in mind. The vision: an agent queries Hub via MCP to discover what capabilities exist, proposes a schema change if something is missing, and implements it once a human approves. AI handles what it's good at while humans retain judgment over API design decisions. Hub is the coordination layer between them.&lt;/p&gt;

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

&lt;p&gt;If you're planning to use AI to accelerate your API development workflow, the investment that will actually move the needle isn't better code generation. It's better coordination infrastructure.&lt;/p&gt;

&lt;p&gt;Federation solved the technical problem of decomposing a graph across teams. It left the organizational problem largely unsolved. That's the gap governance tooling fills.&lt;/p&gt;

&lt;p&gt;LLMs will keep getting better at writing subgraph resolvers. The teams that compound that velocity are the ones that also solve alignment.&lt;/p&gt;




&lt;p&gt;The full article goes deeper into Fission and the architecture of Hub. &lt;a href="https://wundergraph.com/blog/missing-layer-in-graphql-federation" rel="noopener noreferrer"&gt;Read it on the WunderGraph blog.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>apigovernance</category>
      <category>apiarchitecture</category>
      <category>platformengineering</category>
    </item>
    <item>
      <title>How We Built WunderGraph’s Engineering Growth Framework</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Tue, 03 Mar 2026 21:05:22 +0000</pubDate>
      <link>https://dev.to/slickstef11/how-we-built-wundergraphs-engineering-growth-framework-2dj2</link>
      <guid>https://dev.to/slickstef11/how-we-built-wundergraphs-engineering-growth-framework-2dj2</guid>
      <description>&lt;p&gt;At WunderGraph, we’ve nearly tripled our team in under a year. With that growth came the need for a clear &lt;strong&gt;career framework&lt;/strong&gt; to keep hiring fair, growth conversations meaningful, and pay benchmarks consistent for a global team.&lt;/p&gt;

&lt;p&gt;Our &lt;strong&gt;Engineering Growth Framework&lt;/strong&gt; is built around impact—not just what you ship, but how you work and who benefits. It focuses on three areas:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Engineering Excellence
&lt;/h3&gt;

&lt;p&gt;– technical skills, ownership, and problem-solving&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Partnering with Others
&lt;/h3&gt;

&lt;p&gt;– collaboration and knowledge sharing&lt;/p&gt;

&lt;h3&gt;
  
  
  3. WunderGraph Culture
&lt;/h3&gt;

&lt;p&gt;– living our values and ways of working&lt;/p&gt;

&lt;p&gt;It supports two equal tracks—&lt;strong&gt;Individual Contributor&lt;/strong&gt; and &lt;strong&gt;Management&lt;/strong&gt;—with clear steps for progression in both. We already use it in hiring, onboarding, 1:1s, and performance reviews.&lt;br&gt;
The framework is version 1 and will evolve with feedback. Senior roles like Staff and Principal Engineer are calibrated for both technical leadership and strategic alignment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wundergraph.com/jobs" rel="noopener noreferrer"&gt;We’re hiring engineers&lt;/a&gt; who want to grow in an environment built on clarity and impact.&lt;/p&gt;

&lt;p&gt;Full post and public framework here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wundergraph.com/blog/engineering-growth-framework" rel="noopener noreferrer"&gt;The WunderGraph Engineering Growth Framework&lt;/a&gt;&lt;/p&gt;

</description>
      <category>engineeringgrowth</category>
      <category>engineeeringculture</category>
      <category>softwareengineering</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Manifesto That Keeps Our Remote Team Moving Fast</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Wed, 18 Feb 2026 22:18:13 +0000</pubDate>
      <link>https://dev.to/slickstef11/the-manifesto-that-keeps-our-remote-team-moving-fast-1118</link>
      <guid>https://dev.to/slickstef11/the-manifesto-that-keeps-our-remote-team-moving-fast-1118</guid>
      <description>&lt;p&gt;At WunderGraph, we build Cosmo, a GraphQL API management platform for federated graphs at scale. Our team is fully remote—30 people in 9 countries—and we just raised our &lt;a href="https://techcrunch.com/2025/03/27/ebay-backs-wundergraph-to-build-an-open-source-graphql-federation/" rel="noopener noreferrer"&gt;Series A.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we’ve grown, we’ve learned that how we work is just as important as what we ship. So we wrote our &lt;strong&gt;Manifesto&lt;/strong&gt;: 17 beliefs that define how we hire, collaborate, and deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it exists
&lt;/h2&gt;

&lt;p&gt;We want everyone pulling in the same direction, with clarity on what’s expected and what success looks like here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it covers
&lt;/h2&gt;

&lt;p&gt;Ownership. Urgency. Customer obsession. Direct feedback. A willingness to wear any hat. These aren’t slogans—they’re daily habits.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we use it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Shared with every candidate before the first interview&lt;/li&gt;
&lt;li&gt;Part of onboarding for new hires&lt;/li&gt;
&lt;li&gt;Published in our &lt;a href="//notion.so/wundergraph/WunderGraph-Public-Handbook-and-Resources-20db19e0a0ec800690cac27c35fadc73?source=copy_link"&gt;public handbook&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;We’ll review and expand it at our next company retreat in Gran Canaria, evolving it into a values framework that can scale with us.&lt;br&gt;
If you want to work in a place where speed, trust, and impact matter, check out our &lt;a href="https://wundergraph.com/jobs" rel="noopener noreferrer"&gt;open roles.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Full post here:&lt;br&gt;
 👉 &lt;a href="https://wundergraph.com/blog/wundergraph_manifesto" rel="noopener noreferrer"&gt;How Our Core Beliefs Drive the Way We Scale&lt;/a&gt;&lt;/p&gt;

</description>
      <category>culture</category>
      <category>startup</category>
      <category>company</category>
      <category>workplace</category>
    </item>
    <item>
      <title>Compliance Debt in AI Systems</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Wed, 11 Feb 2026 07:28:08 +0000</pubDate>
      <link>https://dev.to/slickstef11/compliance-debt-in-ai-systems-5di4</link>
      <guid>https://dev.to/slickstef11/compliance-debt-in-ai-systems-5di4</guid>
      <description>&lt;p&gt;Every AI deployment now carries audit risk. &lt;/p&gt;

&lt;p&gt;For high-risk AI in Europe, logging and documentation are legally mandated. In the United States, states are building their own rules with no consistency. In parts of Asia, certain high-impact AI systems face mandatory risk assessments and pre-deployment scrutiny.&lt;/p&gt;

&lt;p&gt;Non-compliance blocks sales, delays integrations, and forces engineers to rebuild systems under pressure.&lt;br&gt;
The only sustainable path is making compliance part of the design. Build systems that record evidence as they run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Layers of Evidence
&lt;/h2&gt;

&lt;p&gt;Despite regional differences, every regulatory framework asks for the same proofs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was shipped:&lt;/strong&gt; models, configurations, schemas, and artifacts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who changed it:&lt;/strong&gt; access logs, &lt;a href="https://cosmo-docs.wundergraph.com/studio/rbac" rel="noopener noreferrer"&gt;RBAC&lt;/a&gt;/SSO identities, approvals, version history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How changes were governed:&lt;/strong&gt; linting results, governance checks, signed configs, CI-based controls.&lt;/p&gt;

&lt;p&gt;These layers clarify what auditors mean when they ask for "documentation" or "technical files."&lt;/p&gt;

&lt;h2&gt;
  
  
  EU AI Act
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://artificialintelligenceact.eu/" rel="noopener noreferrer"&gt;EU AI Act&lt;/a&gt; entered into force on August 1, 2024. Key obligations for high-risk AI systems phase in over roughly two to three years, with most provider duties applying from 2026-2027.&lt;/p&gt;

&lt;p&gt;Once obligations begin, high-risk AI providers must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Generate and retain logs for at least six months, longer where necessary based on the system's purpose or other EU/national laws.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep technical documentation and compliance records available for 10 years after the system is placed on the market or put into service.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fines can reach up to &lt;strong&gt;€35 million&lt;/strong&gt; or seven percent of global turnover.&lt;/p&gt;

&lt;h2&gt;
  
  
  US State Approaches
&lt;/h2&gt;

&lt;p&gt;No comprehensive federal law exists. States have taken over.&lt;br&gt;
In the 2025 session, lawmakers in all 50 U.S. states considered at least one AI-related bill or resolution.&lt;/p&gt;

&lt;p&gt;Colorado regulates "high-risk" systems with focus on algorithmic discrimination and transparency, requiring impact assessments, consumer disclosures, and notification to the Attorney General within 90 days when discrimination risks are discovered.&lt;/p&gt;

&lt;p&gt;California targets frontier models rather than use cases. &lt;a href="https://leginfo.legislature.ca.gov/faces/billTextClient.xhtml?bill_id=202520260SB53" rel="noopener noreferrer"&gt;SB 53&lt;/a&gt; requires large frontier-model developers to publish safety/transparency disclosures and report certain critical safety incidents within 15 days, or within 24 hours when there is imminent risk of death or serious injury.&lt;/p&gt;

&lt;p&gt;New York maps high-risk directly to employment. The proposed &lt;a href="https://trackbill.com/bill/new-york-senate-bill-1854-establishes-the-new-york-workforce-stabilization-act-requiring-certain-businesses-to-conduct-artificial-intelligence-impact-assessments-on-the-application-and-use-of-such-artificial-intelligence/2599042/" rel="noopener noreferrer"&gt;Workforce Stabilization Act&lt;/a&gt;  would require AI impact assessments before workplace deployment and impose tax surcharges when AI displaces workers.&lt;/p&gt;

&lt;p&gt;Texas pursued both youth safety and AI governance. The &lt;a href="https://www.lw.com/en/insights/texas-signs-responsible-ai-governance-act-into-law" rel="noopener noreferrer"&gt;Texas Responsible Artificial Intelligence Governance Act&lt;/a&gt;, effective January 1, 2026, establishes statewide consumer protections, defines prohibited AI uses, and creates enforcement mechanisms.&lt;/p&gt;

&lt;p&gt;Utah requires disclosure when consumers interact with generative AI and mandates stricter upfront disclosure for licensed professions and designated 'high-risk' AI interactions.&lt;/p&gt;

&lt;p&gt;Each state treats 'high-risk' differently—employment decisions, youth safety, frontier models, discrimination, transparency. Engineering teams design for multiple compliance targets with no federal standard to unify them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asia Pre-Deployment Review
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.reedsmith.com/articles/navigating-the-complexities-of-ai-regulation-in-china/" rel="noopener noreferrer"&gt;China&lt;/a&gt; mandates lawful training data, consent for personal information, and clear labeling for synthetic content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.meity.gov.in/static/uploads/2024/02/9f6e99572739a3024c9cdaec53a0a0ef.pdf" rel="noopener noreferrer"&gt;India&lt;/a&gt; initially proposed stricter draft guidance pointing toward mandatory government approval for some AI systems in early 2024, then revised its advisory and removed the explicit government-permission language while continuing to emphasize transparency, consent, and content safeguards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.trade.gov/market-intelligence/south-korea-artificial-intelligence-ai-basic-act" rel="noopener noreferrer"&gt;South Korea's AI Basic Act&lt;/a&gt;, effective in 2026, will add mandatory risk assessments and local representation for high-impact systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compliance Costs
&lt;/h2&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.amazonaws.com%2Fuploads%2Farticles%2Fwu2auqlupszl7ss9h9l1.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.amazonaws.com%2Fuploads%2Farticles%2Fwu2auqlupszl7ss9h9l1.png" alt=" " width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Audit-Ready Infrastructure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Identity-Linked Event Trails
&lt;/h3&gt;

&lt;p&gt;Audit-ready logs provide immutable, identity-linked events that allow teams to replay a decision path. If you cannot reconstruct what happened, you cannot satisfy traceability requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Policy Enforcement in CI/CD
&lt;/h3&gt;

&lt;p&gt;Policy enforcement in CI prevents misconfigured models or insecure schemas from entering production. Every blocked change becomes an approval record.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Governance
&lt;/h3&gt;

&lt;p&gt;Access governance connects each action to a verified identity through SSO, SCIM, and RBAC. This creates a chain of accountability that enforcement agencies can verify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning
&lt;/h3&gt;

&lt;p&gt;Versioning links prompts, models, and configurations to their exact commit or revision. This establishes a reproducible audit history for every component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compliance Maturity Model
&lt;/h2&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.amazonaws.com%2Fuploads%2Farticles%2F8q291fi9e1axf060x03o.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.amazonaws.com%2Fuploads%2Farticles%2F8q291fi9e1axf060x03o.png" alt=" " width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit Readiness Checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step One: Map Evidence Gaps
&lt;/h3&gt;

&lt;p&gt;Compare existing logs, approvals, and version history against regional requirements. Identify where traceability breaks or where evidence is missing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step Two: Stabilize the Basics
&lt;/h3&gt;

&lt;p&gt;Ensure six-month log retention, CI policy checks, and identity-based approvals. These controls form the minimum reliable audit trail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step Three: Automate and Operationalize
&lt;/h3&gt;

&lt;p&gt;Add inference-level tracing, compliance coverage KPIs, and region-specific audit bundles. This turns compliance from reactive work into automated evidence generation.&lt;/p&gt;

&lt;p&gt;When proof is generated automatically, regulation stops blocking delivery. Teams that build evidence into their systems answer audit requests in hours. Teams that don't spend weeks reconstructing logs, defending gaps, and explaining why critical evidence doesn't exist.&lt;/p&gt;




&lt;p&gt;Adapted from the original article on the &lt;a href="https://wundergraph.com/blog/ai-non-compliance-costs" rel="noopener noreferrer"&gt;WunderGraph blog.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dataprivacy</category>
      <category>techcompliance</category>
      <category>security</category>
    </item>
    <item>
      <title>Why We Replaced GraphQL Subgraphs with gRPC Services in WunderGraph Cosmo</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Mon, 08 Dec 2025 21:42:11 +0000</pubDate>
      <link>https://dev.to/slickstef11/why-we-replaced-graphql-subgraphs-with-grpc-services-in-wundergraph-cosmo-5ad3</link>
      <guid>https://dev.to/slickstef11/why-we-replaced-graphql-subgraphs-with-grpc-services-in-wundergraph-cosmo-5ad3</guid>
      <description>&lt;p&gt;When teams adopt GraphQL Federation, they usually split their API into Subgraphs. Each Subgraph speaks GraphQL, and the Router stitches them together at runtime.&lt;/p&gt;

&lt;p&gt;But after years of working with Federation, we kept hitting the same limits…Type safety, performance, and the bottlenecks of Subgraph frameworks.&lt;/p&gt;

&lt;p&gt;So we asked:&lt;/p&gt;

&lt;h4&gt;
  
  
  What if Subgraphs didn’t need to use GraphQL at all?
&lt;/h4&gt;

&lt;p&gt;We built a new model in WunderGraph Cosmo. The Router still speaks GraphQL at the edge, but Subgraphs now run as gRPC services behind the scenes.&lt;/p&gt;

&lt;p&gt;Here’s how it works and why we made the switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Subgraphs Look Clean, But Aren’t Type Safe
&lt;/h2&gt;

&lt;p&gt;A Subgraph might start simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type User @key(fields: "id") {
  id: ID!
  name: String!
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But when the Router queries that Subgraph, it sends this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "representations": [
    { "__typename": "User", "id": "1" },
    { "__typename": "User", "id": "2" }
  ]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The _entities field uses a list of _Any. There’s no compile-time check that your resolver handles the shape correctly. You might not catch issues until runtime or production.&lt;/p&gt;

&lt;p&gt;By switching to gRPC, we eliminate this risk. Type mismatches fail at codegen, not later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subgraphs Still Leave You With N+1 Problems
&lt;/h2&gt;

&lt;p&gt;GraphQL has a known issue with N+1 queries. Subgraph frameworks don’t solve this for you; you’re expected to implement a data loader yourself.&lt;/p&gt;

&lt;p&gt;That works fine for small services, but it doesn't scale. Every team ends up solving the same problem, again and again.&lt;/p&gt;

&lt;p&gt;In Cosmo, the Router already handles batching and query planning. If your Subgraphs are gRPC services, they receive batched requests out of the box without needing custom logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Adds Overhead That gRPC Skips
&lt;/h2&gt;

&lt;p&gt;Every Subgraph written in GraphQL has to parse, validate, normalize, and execute the incoming request. That’s a lot of work for a small data fetch.&lt;/p&gt;

&lt;p&gt;Even with an optimized Router, performance can tank if a Subgraph is implemented in a slower language or framework.&lt;/p&gt;

&lt;p&gt;With gRPC, we bypass all that. The Router just sends a single request. There is no parsing or dynamic execution. That saves CPU, latency, and developer effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apollo’s Gatekeeping Slows Everyone Down
&lt;/h2&gt;

&lt;p&gt;One of the biggest issues in the Federation ecosystem is Apollo's control over the Subgraph spec and its frameworks. You can’t ship new features until they do. Even when they do, it takes time for other frameworks to catch up.&lt;/p&gt;

&lt;p&gt;With gRPC, we’re free to move faster. New Router features are immediately usable by any service that supports gRPC—no framework rewrites required.&lt;/p&gt;

&lt;h2&gt;
  
  
  From GraphQL SDL to gRPC in Practice
&lt;/h2&gt;

&lt;p&gt;You still write a GraphQL Subgraph SDL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type User @key(fields: "id") {
  id: ID!
  name: String!
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But instead of implementing that in a Subgraph framework, we compile it to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;syntax = "proto3";
package service;

service UsersService {
  rpc LookupUserById(LookupUserByIdRequest) returns (LookupUserByIdResponse) {}
}

message LookupUserByIdRequestKey {
  string id = 1;
}

message LookupUserByIdRequest {
  repeated LookupUserByIdRequestKey keys = 1;
}

message LookupUserByIdResponse {
  repeated User result = 1;
}

message User {
  reserved 2 to 3;
  string id = 1;
  string name = 4;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repeated field handles batching. The reserved tags prevent field conflicts when evolving the schema. It’s fast, reliable, and strictly typed.&lt;/p&gt;

&lt;p&gt;You can register the service like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;subgraphs:
  - name: users
    routing_url: localhost:4011
    grpc:
      schema_file: ./schema.graphql
      proto_file: ./generated/service.proto
      mapping_file: ./generated/mapping.json

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No runtime SDL parsing. No entity resolution bugs. Just fast, typed calls between the Router and backend services.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Still Write GraphQL. You Just Don’t Implement It.
&lt;/h2&gt;

&lt;p&gt;This doesn’t mean ditching GraphQL entirely. The Router still exposes a GraphQL API to your clients.&lt;/p&gt;

&lt;p&gt;But everything behind the Router can run on gRPC. That makes it faster and more predictable. And it frees your team from framework lock-in.&lt;/p&gt;

&lt;p&gt;If you're curious about how we map GraphQL to gRPC, read the deep dive:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wundergraph.com/blog/graphql-federation-over-grpc" rel="noopener noreferrer"&gt; ➡️ The Next Generation of GraphQL Federation Speaks gRPC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To try it out, start here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cosmo-docs.wundergraph.com/tutorial/grpc-service-quickstart" rel="noopener noreferrer"&gt; ➡️ Cosmo gRPC Quickstart&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the original post, visit &lt;a href="https://wundergraph.com/blog/the-future-of-federation" rel="noopener noreferrer"&gt;WunderGraph’s blog.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;We’re not removing GraphQL. But we are making Subgraphs faster, safer, and easier to evolve. And we believe this model will become the new standard.&lt;/p&gt;

&lt;p&gt;You can follow our work or &lt;a href="https://discord.com/invite/Jjmc8TC" rel="noopener noreferrer"&gt;join our Discord&lt;/a&gt; to chat with the team.&lt;/p&gt;

</description>
      <category>grpc</category>
      <category>graphql</category>
      <category>api</category>
      <category>futureoffederation</category>
    </item>
    <item>
      <title>How We Replaced GraphQL Subgraphs with gRPC for Safer, Faster Federation</title>
      <dc:creator>Stefan  🚀</dc:creator>
      <pubDate>Mon, 08 Dec 2025 20:57:11 +0000</pubDate>
      <link>https://dev.to/slickstef11/how-we-replaced-graphql-subgraphs-with-grpc-for-safer-faster-federation-3fgd</link>
      <guid>https://dev.to/slickstef11/how-we-replaced-graphql-subgraphs-with-grpc-for-safer-faster-federation-3fgd</guid>
      <description>&lt;p&gt;If you've worked with Apollo Federation, you’ve probably used Subgraphs to scale a GraphQL API. But the way entities are resolved between Subgraphs introduces real problems at scale, especially around type safety and correctness.&lt;br&gt;
At WunderGraph, we took a different approach. Instead of resolving entities through GraphQL between services, you can now compile Subgraph SDLs directly into gRPC services. This provides strict typing, built-in batching, and a significantly faster execution model, without compromising the benefits of GraphQL at the router level.&lt;br&gt;
You can read the original deep-dive on our &lt;a href="https://wundergraph.com/blog/graphql-federation-over-grpc" rel="noopener noreferrer"&gt;company blog.&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Entity resolution in Apollo Federation breaks down
&lt;/h3&gt;

&lt;p&gt;In Apollo Federation, each Subgraph defines entities using the &lt;a class="mentioned-user" href="https://dev.to/key"&gt;@key&lt;/a&gt; directive. The router stitches them together using a special _entities field.&lt;/p&gt;

&lt;p&gt;The problem? That _entities field accepts a list of _Any. That means there’s no compile-time validation. You can’t tell whether one Subgraph’s resolver will match the expected shape from another.&lt;/p&gt;

&lt;p&gt;This often leads to subtle runtime bugs, and we've seen this firsthand with teams using Federation at scale.&lt;/p&gt;
&lt;h3&gt;
  
  
  What if your router spoke gRPC instead?
&lt;/h3&gt;

&lt;p&gt;Many teams already use gRPC internally and expose GraphQL through shim layers. Backend engineers prefer gRPC’s type safety and performance. Frontend engineers prefer GraphQL’s flexibility.&lt;/p&gt;

&lt;p&gt;So we built a system that connects both worlds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A compiler that turns GraphQL SDLs into gRPC service definitions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A router adapter that translates GraphQL queries into gRPC calls&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means no more GraphQL-to-GraphQL entity lookups between Subgraphs. You can still write GraphQL at the router boundary, but downstream it’s speaking gRPC.&lt;/p&gt;
&lt;h3&gt;
  
  
  From SDL to gRPC: How it works
&lt;/h3&gt;

&lt;p&gt;Here’s a simple GraphQL Subgraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {
  me: User!
}

type User @key(fields: "id") {
  id: ID!
  name: String!
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our compiler transforms it into this proto file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;syntax = "proto3";
package service;

service UsersService {
  rpc LookupUserById(LookupUserByIdRequest) returns (LookupUserByIdResponse) {}
  rpc QueryMe(QueryMeRequest) returns (QueryMeResponse) {}
}

message LookupUserByIdRequestKey {
  string id = 1;
}

message LookupUserByIdRequest {
  repeated LookupUserByIdRequestKey keys = 1;
}

message LookupUserByIdResponse {
  repeated User result = 1;
}

message QueryMeRequest {}

message QueryMeResponse {
  User me = 1;
}

message User {
  string id = 1;
  string name = 2;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This solves two problems at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests are batched by default, avoiding N+1 issues.&lt;/li&gt;
&lt;li&gt;Everything is strictly typed, so there’s no ambiguity between Subgraphs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of relying on _Any, you now have a method that guarantees structure and order. Backend engineers no longer need custom data loader logic. The router batches calls automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wundergraph.com/blog/graphql-federation-over-grpc" rel="noopener noreferrer"&gt;View full proto examples here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mapping GraphQL types to gRPC
&lt;/h3&gt;

&lt;p&gt;Here’s how the translation works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Object types → protobuf messages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scalars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;String → string&lt;/li&gt;
&lt;li&gt;ID → string&lt;/li&gt;
&lt;li&gt;Int → int32&lt;/li&gt;
&lt;li&gt;Float → float&lt;/li&gt;
&lt;li&gt;Boolean → bool&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Enums → protobuf enums&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Input objects → protobuf request messages&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Queries and Mutations → gRPC service methods&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {
  getBook(id: ID!): Book
}

type Book {
  id: ID!
  title: String!
  pages: Int
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;message GetBookRequest {
  string id = 1;
}

message GetBookResponse {
  Book book = 1;
}

message Book {
  string id = 1;
  string title = 2;
  int32 pages = 3;
}

service QueryService {
  rpc GetBook(GetBookRequest) returns (GetBookResponse);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Schema evolution with proto lock files
&lt;/h3&gt;

&lt;p&gt;Protobuf enforces field numbers. Once you use a number, you can’t reuse it for another field with a different type.&lt;/p&gt;

&lt;p&gt;To manage this, we introduced a &lt;strong&gt;proto lock file&lt;/strong&gt; that reserves previous field numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;message User {
  reserved 2;
  string id = 1;
  int32 age = 3;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents breaking changes if you remove a field and later add a new one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Want to try it?
&lt;/h3&gt;

&lt;p&gt;We’ve published a full &lt;a href="https://cosmo-docs.wundergraph.com/tutorial/grpc-service-quickstart" rel="noopener noreferrer"&gt;quickstart tutorial&lt;/a&gt; and &lt;a href="https://cosmo-docs.wundergraph.com/router/gRPC/grpc-services" rel="noopener noreferrer"&gt;reference docs.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is just the first step. We’re building a new Federation model where GraphQL stays at the edge, and the internals run on fast, type-safe gRPC.&lt;/p&gt;

&lt;p&gt;Let us know what you think:&lt;br&gt;
&lt;a href="https://discord.com/invite/Jjmc8TC" rel="noopener noreferrer"&gt;Join our Discord&lt;/a&gt;&lt;/p&gt;

</description>
      <category>grpc</category>
      <category>graphql</category>
      <category>graphqlfederation</category>
      <category>api</category>
    </item>
  </channel>
</rss>
