English

Unlock the power of GraphQL Federation with Schema Stitching. Learn how to build a unified GraphQL API from multiple services, improving scalability and maintainability.

GraphQL Federation: Schema Stitching - A Comprehensive Guide

In the ever-evolving landscape of modern application development, the need for scalable and maintainable architectures has become paramount. Microservices, with their inherent modularity and independent deployability, have emerged as a popular solution. However, managing numerous microservices can introduce complexities, especially when it comes to exposing a unified API to client applications. This is where GraphQL Federation, and specifically Schema Stitching, comes into play.

What is GraphQL Federation?

GraphQL Federation is a powerful architecture that allows you to build a single, unified GraphQL API from multiple underlying GraphQL services (often representing microservices). It enables developers to query data across different services as if it were a single graph, simplifying the client experience and reducing the need for complex orchestration logic on the client-side.

There are two primary approaches to GraphQL Federation:

This article focuses on Schema Stitching, exploring its concepts, benefits, limitations, and practical implementation.

Understanding Schema Stitching

Schema Stitching is the process of merging multiple GraphQL schemas into a single, cohesive schema. This unified schema acts as a facade, hiding the complexity of the underlying services from the client. When a client makes a request to the stitched schema, the gateway intelligently routes the request to the appropriate underlying service(s), retrieves the data, and combines the results before returning them to the client.

Think of it like this: You have multiple restaurants (services) each specializing in different cuisines. Schema Stitching is like a universal menu that combines all the dishes from each restaurant. When a customer (client) orders from the universal menu, the order is intelligently routed to the appropriate restaurant kitchens, the food is prepared, and then combined into a single delivery for the customer.

Key Concepts in Schema Stitching

Benefits of Schema Stitching

Schema Stitching offers several compelling benefits for organizations adopting a microservices architecture:

Limitations of Schema Stitching

While Schema Stitching offers numerous advantages, it's important to be aware of its limitations:

Practical Implementation of Schema Stitching

Let's walk through a simplified example of how to implement Schema Stitching using Node.js and the graphql-tools library (a popular choice for schema stitching). This example involves two microservices: a User Service and a Product Service.

1. Define the Remote Schemas

First, define the GraphQL schemas for each of the remote services.

User Service (user-service.js):


const { buildSchema } = require('graphql');

const userSchema = buildSchema(`
  type User {
    id: ID!
    name: String
    email: String
  }

  type Query {
    user(id: ID!): User
  }
`);

const users = [
  { id: '1', name: 'Alice Smith', email: 'alice@example.com' },
  { id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
];

const userRoot = {
  user: (args) => users.find(user => user.id === args.id),
};

module.exports = {
  schema: userSchema,
  rootValue: userRoot,
};

Product Service (product-service.js):


const { buildSchema } = require('graphql');

const productSchema = buildSchema(`
  type Product {
    id: ID!
    name: String
    price: Float
    userId: ID!  # Foreign key to User Service
  }

  type Query {
    product(id: ID!): Product
  }
`);

const products = [
  { id: '101', name: 'Laptop', price: 1200, userId: '1' },
  { id: '102', name: 'Smartphone', price: 800, userId: '2' },
];

const productRoot = {
  product: (args) => products.find(product => product.id === args.id),
};

module.exports = {
  schema: productSchema,
  rootValue: productRoot,
};

2. Create the Gateway Service

Now, create the gateway service that will stitch the two schemas together.

Gateway Service (gateway.js):


const { stitchSchemas } = require('@graphql-tools/stitch');
const { makeRemoteExecutableSchema } = require('@graphql-tools/wrap');
const { graphqlHTTP } = require('express-graphql');
const express = require('express');
const { introspectSchema } = require('@graphql-tools/wrap');
const { printSchema } = require('graphql');
const fetch = require('node-fetch');

async function createRemoteSchema(uri) {
  const fetcher = async (params) => {
    const response = await fetch(uri, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(params),
    });
    return response.json();
  };

  const schema = await introspectSchema(fetcher);
  return makeRemoteExecutableSchema({
    schema,
    fetcher,
  });
}

async function main() {
  const userSchema = await createRemoteSchema('http://localhost:4001/graphql');
  const productSchema = await createRemoteSchema('http://localhost:4002/graphql');

  const stitchedSchema = stitchSchemas({
    subschemas: [
      { schema: userSchema },
      { schema: productSchema },
    ],
    typeDefs: `
      extend type Product {
        user: User
      }
    `,
    resolvers: {
      Product: {
        user: {
          selectionSet: `{ userId }`,
          resolve(product, args, context, info) {
            return info.mergeInfo.delegateToSchema({
              schema: userSchema,
              operation: 'query',
              fieldName: 'user',
              args: {
                id: product.userId,
              },
              context,
              info,
            });
          },
        },
      },
    },
  });

  const app = express();
  app.use('/graphql', graphqlHTTP({
    schema: stitchedSchema,
    graphiql: true,
  }));

  app.listen(4000, () => console.log('Gateway server running on http://localhost:4000/graphql'));
}

main().catch(console.error);

3. Run the Services

You'll need to run the User Service and Product Service on different ports. For example:

User Service (port 4001):


const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./user-service');

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true,
}));

app.listen(4001, () => console.log('User service running on http://localhost:4001/graphql'));

Product Service (port 4002):


const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./product-service');

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true,
}));

app.listen(4002, () => console.log('Product service running on http://localhost:4002/graphql'));

4. Query the Stitched Schema

Now you can query the stitched schema through the gateway (running on port 4000). You can run a query like this:


query {
  product(id: "101") {
    id
    name
    price
    user {
      id
      name
      email
    }
  }
}

This query retrieves the product with ID "101" and also fetches the associated user from the User Service, demonstrating how Schema Stitching allows you to query data across multiple services in a single request.

Advanced Schema Stitching Techniques

Beyond the basic example, here are some advanced techniques that can be used to enhance your Schema Stitching implementation:

Choosing Between Schema Stitching and Apollo Federation

While Schema Stitching is a viable option for GraphQL Federation, Apollo Federation has become the more popular choice due to its advanced features and improved developer experience. Here's a comparison of the two approaches:

Feature Schema Stitching Apollo Federation
Schema Definition Uses existing GraphQL schema language Uses a declarative schema language with directives
Query Planning Requires manual query delegation Automatic query planning by the Apollo Gateway
Type Extensions Limited support Built-in support for type extensions
Key Directives Not supported Uses @key directive to identify entities
Distributed Tracing Requires manual implementation Built-in support for distributed tracing
Tooling and Ecosystem Less mature tooling More mature tooling and a large community
Complexity Can be complex to manage in large systems Designed for large and complex systems

When to Choose Schema Stitching:

When to Choose Apollo Federation:

Real-World Examples and Use Cases

Here are some real-world examples of how GraphQL Federation, including Schema Stitching, can be used:

Best Practices for Schema Stitching

To ensure a successful Schema Stitching implementation, follow these best practices:

Conclusion

GraphQL Federation with Schema Stitching offers a powerful approach to building unified APIs from multiple services in a microservices architecture. By understanding its core concepts, benefits, limitations, and implementation techniques, you can leverage Schema Stitching to simplify data access, improve scalability, and enhance maintainability. While Apollo Federation has emerged as a more advanced solution, Schema Stitching remains a viable option for simpler scenarios or when integrating existing GraphQL services. Carefully consider your specific needs and requirements to choose the best approach for your organization.