iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Schema Stitching

GraphQL schema stitching: merge multiple schemas into one. Legacy alternative to federation; still useful when subgraphs cannot be modified.

GraphQL — schema stitching

EXAMPLE
// ===== Stitching vs federation =====
// Federation: subgraphs OPT IN with @key directives; modern, declarative
// Stitching:  gateway combines schemas WITHOUT changes to subgraphs
//
// Stitching is useful when:
// - You cannot modify the upstream services
// - You wrap legacy REST APIs as GraphQL
// - You merge a 3rd-party GraphQL endpoint with your own

// ===== Setup with graphql-tools =====
// npm install @graphql-tools/stitch @graphql-tools/wrap @graphql-tools/url-loader graphql

import { stitchSchemas } from '@graphql-tools/stitch';
import { loadSchema } from '@graphql-tools/load';
import { UrlLoader } from '@graphql-tools/url-loader';
import { wrapSchema, RenameTypes } from '@graphql-tools/wrap';
import { print } from 'graphql';

// Load subgraphs:
async function getRemoteSchema(url) {
  const executor = async ({ document, variables }) => {
    const query = print(document);
    const r = await fetch(url, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ query, variables }),
    });
    return r.json();
  };
  const schema = await loadSchema(url, { loaders: [new UrlLoader()] });
  return wrapSchema({ schema, executor });
}

const usersSchema = await getRemoteSchema('http://users:4001/graphql');
const ordersSchema = await getRemoteSchema('http://orders:4002/graphql');

const stitched = stitchSchemas({
  subschemas: [
    { schema: usersSchema },
    { schema: ordersSchema },
  ],
});

// ===== Resolve cross-schema relationships =====
const stitched2 = stitchSchemas({
  subschemas: [
    { schema: usersSchema, merge: { User: { fieldName: 'user', selectionSet: '{ id }', args: (parent) => ({ id: parent.id }) } } },
    { schema: ordersSchema, merge: { Order: { fieldName: 'order', selectionSet: '{ id }', args: (parent) => ({ id: parent.id }) } } },
  ],
  typeDefs: \`
    extend type User { orders: [Order!]! }
  \`,
  resolvers: {
    User: {
      orders: (user, _args, ctx, info) => info.mergeInfo.delegateToSchema({
        schema: ordersSchema,
        operation: 'query',
        fieldName: 'ordersByUser',
        args: { userId: user.id },
        context: ctx,
        info,
      }),
    },
  },
});

// ===== Type renaming + filtering =====
// Avoid name collisions:
const renamed = wrapSchema({
  schema: legacySchema,
  transforms: [new RenameTypes((name) => 'Legacy' + name)],
});

// ===== Serve the gateway =====
import { createYoga } from 'graphql-yoga';
import { createServer } from 'node:http';

const yoga = createYoga({ schema: stitched });
createServer(yoga).listen(4000);

// ===== Caching =====
// Per-request DataLoaders BELOW the stitching layer.
// HTTP cache on subschemas via standard headers.

// ===== When stitching wins =====
// - Cannot change upstream schemas
// - Wrap legacy REST as GraphQL (via @graphql-tools/wrap)
// - Internal proof-of-concept of a unified graph

// ===== When federation wins =====
// - You own all subgraphs
// - Need clear ownership boundaries
// - Want a schema registry + composition checks
// - Apollo Studio / GraphOS tooling

// ===== Patterns =====
// - Use Apollo Federation if you control the subgraphs
// - Use stitching for legacy / third-party integrations
// - Rename types to avoid collisions
// - DataLoader at subschema layer for batching

// ===== Pitfalls =====
// - Long resolver chains -> slow queries (use field-level caching)
// - N+1 across schemas; mitigate with DataLoader
// - No global error handling -> upstream errors leak
// - Versioning: changing upstream schema breaks the gateway

Why it matters

Stitching merges schemas without changing subgraphs — useful for legacy + third-party integrations. Federation is the modern path when you own the subgraphs. graphql-tools + Yoga is the cleanest stitching stack; mind type collisions and N+1 across schemas.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# Older approach — merge multiple schemas at runtime.
# Federation is preferred for new projects.
Try it Yourself »

Discussion

Loading…