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…