Introduction

GraphQL has gained significant popularity in recent years as a flexible and efficient way to query and manipulate data from various sources. One of the key advantages of GraphQL is its ability to serve as a gateway that combines, stitches, or merges data from multiple data sources into a single unified API. In this article, we will explore different approaches to building a GraphQL gateway and how they enable developers to create powerful, unified APIs that bring together data from diverse sources.

What is a GraphQL Gateway?

A GraphQL gateway is a server that acts as an intermediary between clients and various data sources, including databases, REST APIs, third-party services, and more. It receives GraphQL queries from clients and resolves those queries by fetching data from the appropriate sources. The key benefits of using a GraphQL gateway include:

  1. Single Endpoint: Clients interact with a single GraphQL endpoint, simplifying the API consumption process.
  2. Reduced Over-fetching: Clients can request only the data they need, minimizing over-fetching of data.
  3. Data Aggregation: A GraphQL gateway can combine data from multiple sources into a single response, providing a unified view of the data.
  4. Consistency: The GraphQL schema enforces a consistent and strongly typed API, making it easier to understand and use.

Now, let’s explore three common approaches to building a GraphQL gateway: combining, stitching, and merging data sources.

Combining Data Sources

In the “combining” approach, a GraphQL gateway fetches data from multiple data sources independently and then merges the results to respond to a single GraphQL query. This approach is suitable when each data source can be queried separately, and the final result can be aggregated without complex transformations.

Use Case: Combining REST APIs

Imagine you have several REST APIs that provide information about products, users, and reviews. You can create a GraphQL gateway that makes individual requests to these REST endpoints and combines the results.

Here’s a simplified example using Apollo Server and Node.js:

javascript
const { ApolloServer, gql } = require('apollo-server');
const fetch = require('node-fetch');
const typeDefs = gql`
type Product {
id: ID
name: String
price: Float
reviews: [Review]
}

type Review {
id: ID
text: String
}

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

const resolvers = {
Query: {
product: async (_, { id }) => {
const productResponse = await fetch(`https://products-api.com/products/${id}`);
const productData = await productResponse.json();

const reviewResponse = await fetch(`https://reviews-api.com/reviews?productId=${id}`);
const reviewData = await reviewResponse.json();

return {
id: productData.id,
name: productData.name,
price: productData.price,
reviews: reviewData,
};
},
},
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
console.log(`GraphQL Gateway ready at ${url}`);
});

In this example, the GraphQL gateway queries two separate REST APIs (products and reviews) and combines the data to respond to the product query.

Stitching Data Sources

The “stitching” approach involves composing a single GraphQL schema from multiple underlying schemas representing different data sources. Instead of fetching data independently, the gateway delegates parts of the query to the appropriate schemas. This approach is beneficial when data sources have complex interactions or when schemas need to be customized.

Use Case: Schema Stitching for Microservices

Consider a microservices architecture where each microservice has its own GraphQL schema. You can use schema stitching to combine these schemas into a single unified GraphQL API.

Here’s a simplified example using Apollo Server and schema stitching:

javascript
const { ApolloServer, gql } = require('apollo-server');
const { stitchSchemas } = require('@graphql-tools/stitch');
const productsSchema = buildProductsSchema(); // Define product schema
const usersSchema = buildUsersSchema(); // Define user schema
const reviewsSchema = buildReviewsSchema(); // Define reviews schema

const gatewaySchema = stitchSchemas({
subschemas: [
{ schema: productsSchema },
{ schema: usersSchema },
{ schema: reviewsSchema },
],
typeDefs: gql`
type Query {
product(id: ID): Product
user(id: ID): User
}
`
,
});

const server = new ApolloServer({ schema: gatewaySchema });

server.listen().then(({ url }) => {
console.log(`GraphQL Gateway ready at ${url}`);
});

In this example, we create separate schemas for products, users, and reviews and then use schema stitching to combine them into a single gateway schema. Clients can query this gateway schema to retrieve data from the microservices.

Merging Data Sources

The “merging” approach involves creating a single data source that abstracts and merges data from multiple underlying sources, treating them as a single entity. This approach is useful when you want to simplify the client’s perspective and present a unified view of the data.

Use Case: Merging Databases

Imagine you have two databases—one containing customer information and another containing order information. You want to create a GraphQL gateway that presents a merged view of customers and their orders.

Here’s a simplified example using Apollo Server and Node.js:

javascript

const { ApolloServer, gql } = require('apollo-server');

const customersDatabase = {
‘1’: { id: ‘1’, name: ‘Alice’ },
‘2’: { id: ‘2’, name: ‘Bob’ },
};

const ordersDatabase = {
‘101’: { id: ‘101’, customerId: ‘1’, totalAmount: 50 },
‘102’: { id: ‘102’, customerId: ‘1’, totalAmount: 75 },
‘103’: { id: ‘103’, customerId: ‘2’, totalAmount: 30 },
};

const typeDefs = gql`
type Customer {
id: ID
name: String
orders: [Order]
}

type Order {
id: ID
totalAmount: Float
}

type Query {
customer(id: ID): Customer
}
`;

const resolvers = {
Customer: {
orders: (customer) => {
return Object.values(ordersDatabase).filter((order) => order.customerId === customer.id);
},
},
Query: {
customer: (_, { id }) => customersDatabase[id],
},
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
console.log(`GraphQL Gateway ready at ${url}`);
});

In this example, we merge data from two databases (customers and orders) into a single GraphQL schema. The Customer type includes a field that resolves orders associated with each customer.

Conclusion

Building a GraphQL gateway is a powerful way to combine, stitch, or merge data from various sources into a unified API. Each approach—combining, stitching, and merging—serves specific use cases and requirements. The choice of which approach to use depends on factors like the complexity of data interactions, data source flexibility, and the desired client experience.

Whether you’re building a GraphQL gateway for REST APIs, microservices, databases, or other data sources, GraphQL’s flexibility and the available tooling make it a compelling choice for creating unified and efficient APIs that meet the needs of modern applications. As you embark on your GraphQL gateway journey, consider the specific demands of your project and choose the approach that best aligns with your goals and constraints.