AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Api Graphql Federation

skill-j4flmao-agent-skills-graphql-federation · by j4flmao

>

No reviews yet
0 installs
9 views
0.0% view→install

Install

$ agentstack add skill-j4flmao-agent-skills-graphql-federation

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-j4flmao-agent-skills-graphql-federation)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Api Graphql Federation? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

GraphQL Federation

Purpose

Design and implement GraphQL Federation: compose multiple subgraphs into a unified supergraph, manage distributed GraphQL architecture, and scale GraphQL across teams. Make informed build-vs-buy decisions between Apollo Federation and alternative distributed graph strategies.

Workflow

Federation Architecture

Supergraph (Apollo Router / Gateway)
  ├── Subgraph A (Users service) — @key(fields: "id")
  ├── Subgraph B (Orders service) — @key(fields: "id") @extends User
  ├── Subgraph C (Reviews service) — @key(fields: "id") @extends User
  └── Subgraph D (Inventory service) — standalone

Apollo Federation vs. GraphQL Mesh Decision Tree

Use this decision framework when choosing between Apollo Federation and GraphQL Mesh:

| Criterion | Apollo Federation | GraphQL Mesh | |-----------|-----------------|--------------| | Schema ownership | Each subgraph owns its schema | Sources are existing APIs (REST, gRPC, SOAP, etc.) | | Team topology | Multiple teams own separate subgraphs | Single team integrating existing backends | | Source types | Native GraphQL only | Any source (REST, OpenAPI, gRPC, SQL, SOAP, etc.) | | Composition model | Static composition via Rover CLI | Runtime schema stitching | | Gateway | Apollo Router (Rust) or Gateway (Node.js) | Envelop, Yoga, or custom | | Federation directives | @key, @extends, @requires, @provides, @shareable | Transformers, handlers, mesh config | | Entity resolution | Built-in via __resolveReference | Manual stitching resolvers | | Query planning | Automatic query planner | Manual or custom merge config | | Performance | Optimized Rust router, plan caching | Depends on underlying gateway | | Ecosystem maturity | Mature (Apollo GraphOS, Studio) | Growing (The Guild ecosystem) | | When to choose | Greenfield GraphQL with multiple teams | Wrapping legacy/heterogeneous backends |

Decision flow:

  1. Are all your sources already GraphQL? → Apollo Federation
  2. Do you need to wrap REST/gRPC/SQL backends? → GraphQL Mesh
  3. Do you have 3+ teams owning separate domains? → Apollo Federation
  4. Is your primary goal API unification of legacy systems? → GraphQL Mesh
  5. Hybrid approach: Use Mesh to convert REST → GraphQL, then compose those as subgraphs via Federation

Federation Directives (Federation v2)

| Directive | Purpose | Example | |-----------|---------|---------| | @key | Primary key for entity | @key(fields: "id") | | @extends | Extend type from another subgraph (implied in v2) | type User @key(fields: "id") | | @external | Field defined in another subgraph (implied in v2) | id: ID! | | @requires | Field requires data from another subgraph | @requires(fields: "shippingZip") | | @provides | Field provides data to other subgraphs | @provides(fields: "name") | | @shareable | Field can be resolved by multiple subgraphs | @shareable | | @override | Migrate field resolution between subgraphs | @override(from: "inventory") | | @inaccessible | Hide field from supergraph | @inaccessible | | @composeDirective | Propagate directive to supergraph | @composeDirective(name: "@authorized") | | @interfaceObject | Expose interface fields on entity | @interfaceObject |

Subgraph Schema Example (Federation v2)

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

# Orders subgraph (extends User — no @extends needed in v2)
type User @key(fields: "id") {
    id: ID!
    orders: [Order!]!
}

type Order @key(fields: "id") {
    id: ID!
    userId: ID!
    total: Float!
    status: OrderStatus!
}

Entity Resolution Deep Dive

Each subgraph that extends an entity must implement __resolveReference:

// Users subgraph — entity origin
const resolvers = {
  User: {
    __resolveReference(ref, context) {
      return context.dataSources.users.findById(ref.id);
    },
  },
};

// Orders subgraph — entity extension
const resolvers = {
  User: {
    __resolveReference(ref, context) {
      // Return just enough to resolve orders
      return { id: ref.id };
    },
    orders(parent, _, context) {
      return context.dataSources.orders.findByUserId(parent.id);
    },
  },
};

Resolution flow:

  1. Router receives query spanning multiple subgraphs
  2. Router sends _entities query to Orders subgraph with representations [{"__typename": "User", "id": "1"}]
  3. Subgraph calls __resolveReference with each representation
  4. Subgraph returns the entity with only the fields requested from it
  5. Router merges fields from all subgraph responses into a unified result

@requires Resolution Flow

# Shipping subgraph needs weight from another subgraph
type Product @key(fields: "id") {
  id: ID!
  weight: Int @external
  shippingCost: Float @requires(fields: "weight")
}
  1. Router fetches weight from the subgraph that owns it
  2. Router includes weight in the representation sent to Shipping subgraph
  3. Shipping subgraph receives {"__typename": "Product", "id": "1", "weight": 10}
  4. Computes shippingCost using the pre-fetched weight

Supergraph Composition Pipeline

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ Subgraph A   │    │ Subgraph B   │    │ Subgraph C   │
│ Schema       │    │ Schema       │    │ Schema       │
└──────┬───────┘    └──────┬───────┘    └──────┬───────┘
       │                   │                   │
       └───────────────────┼───────────────────┘
                           ▼
                   ┌───────────────┐
                   │  Composition  │  rover supergraph compose
                   │    Engine     │
                   └───────┬───────┘
                           ▼
                   ┌───────────────┐
                   │   Supergraph  │  unified schema sent to router
                   │    Schema     │
                   └───────────────┘
# supergraph.yaml
federation_version: 2
subgraphs:
  accounts:
    routing_url: http://accounts:4001/graphql
    schema:
      file: ./schemas/accounts.graphql
  products:
    routing_url: http://products:4002/graphql
    schema:
      file: ./schemas/products.graphql
  orders:
    routing_url: http://orders:4003/graphql
    schema:
      file: ./schemas/orders.graphql
rover supergraph compose --config ./supergraph.yaml > supergraph.graphql

Schema Evolution & Breaking Change Detection

# Check backward compatibility before deploying
rover subgraph check my-graph@current \
    --schema ./updated-accounts.graphql \
    --name accounts

# If check passes, publish the new schema
rover subgraph publish my-graph@current \
    --schema ./updated-accounts.graphql \
    --name accounts \
    --routing-url http://accounts:4001/graphql

What rover subgraph check validates:

  • Field/type removals
  • Argument changes
  • @key directive changes
  • Value type to entity conversion
  • Enum value additions/removals

Federated Tracing & Observability

OpenTelemetry in Apollo Router
# router.yaml — federated tracing
telemetry:
  tracing:
    otlp:
      endpoint: http://otel-collector:4318
      protocol: http
    propagation:
      context: cloudtrace
      # Forward trace context to subgraphs
      forward:
        - "traceparent"
        - "x-cloud-trace-context"
    sampling:
      default: 0.1
      subgraphs:
        accounts: 1.0       # Trace all accounts requests
        reviews: 0.01       # Sample reviews at 1%

What federated tracing reveals:

  • Query plan generation — time spent planning the multi-subgraph fetch
  • Per-subgraph latency — which subgraph is the bottleneck
  • Fetch boundary count — number of subgraph hops per query
  • Entity resolution — time spent in __resolveReference
  • Representation passing — overhead of data transfer between services
Apollo Studio Integration
# router.yaml
telemetry:
  apollo:
    graphs:
      - graph_ref: my-graph@current
        key: ${APOLLO_KEY}
    field_usage: true        # Track field-level usage stats
    operation_counts: true   # Track operation frequency

Studio provides:

  • Field usage heatmaps — which fields are most requested
  • Operation traces — waterfall view across subgraphs
  • Schema checks — breaking change detection in CI
  • Cost analysis — query complexity scoring

Security in Federated Architecture

Layered Security Model
Client → Router (authN + rate limit) → Subgraph (authZ + validation)
Router-Level Authentication (JWT)
# router.yaml
authentication:
  jwt:
    jwks_urls:
      - https://auth.example.com/.well-known/jwks.json
    issuer: https://auth.example.com/
    audiences:
      - my-api

headers:
  all:
    request:
      - propagate:
          matching: .*
      - insert:
          name: "x-user-id"
          value: "{{ authentication.jwt.sub }}"
      - insert:
          name: "x-user-roles"
          value: "{{ authentication.jwt.claims.roles }}"
Subgraph-Level Authorization
// Each subgraph independently validates permissions
const resolvers = {
  Query: {
    userOrders: async (_, { userId }, { userId: authUserId, roles }) => {
      if (authUserId !== userId && !roles.includes('admin')) {
        throw new GraphQLError('Forbidden', {
          extensions: { code: 'FORBIDDEN' },
        });
      }
      return db.orders.findByUserId(userId);
    },
  },
  User: {
    __resolveReference(ref, { userId, roles }) {
      // Guard entity resolution — don't leak existence
      if (!userId) return null;
      return db.users.findById(ref.id);
    },
  },
};
Protecting Against GraphQL-Specific Attacks

| Attack | Mitigation | Config | |--------|-----------|--------| | Deeply nested queries | Depth limiting | max_depth: 10 | | Query aliasing abuse | Alias limiting | max_aliases: 15 | | Entity list bombing | Max entities per request | max_entities_per_request: 100 | | Introspection scraping | Disable in production | introspection: false | | Costly queries | Cost analysis | demand_control.strategy: cost_bound |

# router.yaml — demand control
demand_control:
  strategy: cost_bound
  list_cost: 1
  object_cost: 2
  scoring:
    max_cost: 1000
    max_depth: 10
  reject_on_limit_exceeded: true

rate_limiting:
  global:
    capacity: 1000
    time_window: 60s
  per_user:
    capacity: 100
    time_window: 60s

Production Considerations

Traffic Shaping Per Subgraph
traffic_shaping:
  all:
    timeout: 30s
    compression: true
    http2:
      keepalive_interval: 30s
      keepalive_timeout: 10s
  subgraphs:
    accounts:
      timeout: 5s
      retry:
        max_retries: 3
        base_interval: 100ms
    reviews:
      timeout: 3s
      circuit_breaker:
        error_threshold: 0.5
        request_volume_threshold: 20
        sleep_window: 30s
        half_open_requests: 5
    inventory:
      timeout: 10s
      retry:
        max_retries: 2
Query Plan Caching
query_planning:
  cache:
    enabled: true
    size: 10000
    ttl: 3600s
  experimental_plans: false
  incremental_delivery:
    enable_single_entity: true

Warm the cache on deploy:

// Pre-warm common query plans
const commonQueries = [
  `query { me { id name } }`,
  `query { products(first: 10) { id name price } }`,
  `query { order(id: "hot") { id status total } }`,
];
await Promise.all(commonQueries.map(q => router.execute(q)));
Blue-Green Supergraph Deployment
# 1. Compose new supergraph
rover supergraph compose --config ./supergraph.v2.yaml --output supergraph.v2.graphql

# 2. Deploy to staging router
cp supergraph.v2.graphql /etc/apollo/supergraph.staging.graphql

# 3. Health check staging router
curl -f http://localhost:4001/.well-known/apollo/server-health

# 4. Promote to production
cp supergraph.v2.graphql /etc/apollo/supergraph.graphql

# 5. Reload production router
kill -HUP $(cat /var/run/apollo-router.pid)

Performance Budgets

performance_budget:
  max_fetch_count: 5        # Max subgraph hops per query
  max_query_depth: 8        # Max nesting depth
  max_latency_p99_ms: 500   # P99 latency across all subgraphs
  max_cost_per_query: 100   # Cost analysis limit
Reducing Fetch Boundaries with @provides
# Products subgraph: users browsing products don't hit accounts
type Query {
  topProducts: [Product!]!
}

extend type Product @key(fields: "id") {
  id: ID! @external
  name: String! @external @provides(fields: "name")
  price: Float! @provides(fields: "currency")
}

DataLoader Across Subgraphs

// Batch-load entities to avoid N+1 resolution calls
class UserLoader {
  private batch = new Map>();

  load(id: string): Promise {
    if (!this.batch.has(id)) {
      this.batch.set(id, this.fetchBatch());
    }
    return this.batch.get(id)!;
  }

  private async fetchBatch(): Promise {
    const ids = [...this.batch.keys()];
    const users = await db.users.findByIds(ids);
    for (const user of users) {
      this.batch.set(user.id, Promise.resolve(user));
    }
  }
}

Testing Federated Graphs

Unit Test: Subgraph Entity Resolution
describe('Accounts Subgraph', () => {
  it('resolves User entity by key', async () => {
    const result = await subgraph.executeQuery(`
      query ($representations: [_Any!]!) {
        _entities(representations: $representations) {
          ... on User { id name email }
        }
      }
    `, {
      representations: [{ __typename: 'User', id: '1' }],
    });

    expect(result.data._entities[0].name).toBe('Alice');
  });
});
Integration Test: Cross-Subgraph Query
describe('Supergraph: User with Orders', () => {
  it('resolves fields across subgraphs', async () => {
    const query = `
      query { user(id: "1") { name orders { total status } } }
    `;
    const result = await gateway.execute(query);
    expect(result.data.user.name).toBe('Alice');
    expect(result.data.user.orders).toHaveLength(3);
  });
});
Contract Testing Between Subgraphs
// Each subgraph publishes its schema contract
// CI validates that contracts remain compatible
describe('Contracts', () => {
  it('accounts subgraph schema is valid', async () => {
    const schema = fs.readFileSync('./schemas/accounts.graphql', 'utf8');
    const errors = await validateSchema(schema);
    expect(errors).toHaveLength(0);
  });

  it('orders extension of User is compatible', () => {
    // Verify @key fields match across subgraphs
    const userKey = extractKey(accountsSchema, 'User');
    const orderUserKey = extractKey(ordersSchema, 'User');
    expect(userKey).toEqual(orderUserKey);
  });
});

Common Composition Errors

| Error | Cause | Fix | |-------|-------|-----| | ENUM_MISMATCH | Enum values differ between subgraphs | Align enum definitions | | TYPE_MISMATCH | Same name used for type vs interface | Unify type kind | | EXTERNAL_MISSING | Referenced field not @external | Mark field as external | | KEY_MISSING | Type extended but no @key in origin | Add @key to origin | | REQUIRES_MISSING | @requires field unavailable | Ensure field is resolvable | | DUPLICATE_FIELD | Field defined in multiple subgraphs without @shareable | Add @shareable |

Migration Guide: Federation 1 → Federation 2

| Federation 1 | Federation 2 | |-------------|-------------| | @extends | Unnecessary (all type extensions are implicit) | | @external | Unnecessary (fields owned elsewhere assumed external) | | gateway.js | @apollo/gateway v2+ handles automatically | | Composition | rover supergraph compose with federation_version: 2 |

Steps:

  1. Update federation_version to 2 in supergraph config
  2. Remove all @extends and @external directives from subgraph schemas
  3. Update to @apollo/gateway@^2.0 or deploy Apollo Router
  4. Run rover supergraph compose to validate
  5. Deploy ne

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.