DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • Pagination in GraphQL: Efficiently Retrieve and Manipulate Data
  • Why and When to Use GraphQL
  • Consuming GraphQL API With React.js
  • An In-Depth Analysis of GraphQL Functioning Using GenAI Within a Monolithic Application Framework

Trending

  • Handling “Element Is Not Clickable at Point” Exception in Selenium
  • A Comprehensive Guide To Building and Managing a White-Label Platform
  • Test Smells: Cleaning up Unit Tests
  • Data Governance – Data Privacy and Security – Part 1
  1. DZone
  2. Data Engineering
  3. Data
  4. Merge GraphQL Schemas Using Apollo Server and Koa

Merge GraphQL Schemas Using Apollo Server and Koa

The article provides a step-by-step guide on how to merge schemas using these tools, making it easier for developers to organize and scale their GraphQL APIs.

By 
Anton Kalik user avatar
Anton Kalik
·
May. 25, 23 · Tutorial
Like (4)
Save
Tweet
Share
4.6K Views

Join the DZone community and get the full member experience.

Join For Free

Today, in our modern developer world, it is absolutely impossible to imagine life without such technologies as React, Node JS, GraphQL, and so on. They have solid ranks and are holding leading positions in data delivery. 70% of the cases I come across are projects that are integrated with GraphQL or are about to migrate to it. More and more companies prefer to use the GraphQL data query syntax, and today it is a piece of must-have knowledge.

GraphQL is a query-typed language for API which is widely used for requesting data from the server side to the client side in optimized mater. Clients request exactly what they need using typed schema. It allows you to send only what was requested instead of a fixed dataset.

Apollo Server gives you tools for sending responses to client requests. Apollo Client gives the ability to use GraphQL API, including cache and linking.

What Is It about?

We gonna create two Apollo Servers, which going to handle the GraphQL schema merge. It’s a situation when some external server responds to GraphQL API and some other service uses its own GraphQL schema, including external schema. On the Node layer, we going to wrap results up from the external server in one schema and send it to the client. Literally, we gonna just merge two schemas into one and send it to the client.

Let’s Dive Into the Code

For the implementation, we going to use NodeJS environment, Koa middleware, and Apollo Server with GraphQL Tools.

We have to run two servers. Both have to have a GraphQL Apollo Server. Here is the diagram.

Server Diagram

Time to create boilerplates and run them both. For that, we need to create two folders and name one folder something like this: boilerplate-raphql-koa-server-external and the second folder just like this: boilerplate-graphql-koa-server

Before starting, please take a look at the folder structure in both projects. Pretty straightforward. The difference between those two repos is going to be in the code.

Plain Text
 
├── package.json
└── src
    ├── index.js
    ├── resolvers.js
    └── schema.js


External GraphQL Server

Now, let’s set up the boilerplate-graphql-koa-server-external

JSON
 
{
  "name": "boilerplate-graphql-koa-server-external",
  "version": "1.0.0",
  "description": "Boilerplate GraphQL Koa server external",
  "main": "src/index.js",
  "scripts": {
    "start": "PORT=4000 node src/index.js"
  },
  "engines": {
    "node": "16.17.x"
  },
  "dependencies": {
    "@graphql-tools/schema": "^9.0.2",
    "@koa/cors": "^3.4.1",
    "apollo-server-core": "^3.10.2",
    "apollo-server-koa": "^3.10.2",
    "graphql": "^15.8.0",
    "koa": "^2.13.4",
    "koa-graphql": "^0.12.0"
  }
}


Then let’s create the server itself. In the src folder in theindex.js add server setup:

JavaScript
 
const Koa = require('koa');
const http = require('http');
const cors = require('@koa/cors');
const { ApolloServer } = require('apollo-server-koa');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');

async function server({ typeDefs, resolvers }) {
  const app = new Koa();
  const httpServer = http.createServer();
  const apolloServer = new ApolloServer({
    introspection: true,
    schema: makeExecutableSchema({
      typeDefs,
      resolvers,
    }),
  });

  await apolloServer.start();
  apolloServer.applyMiddleware({ app, path: '/api/v1/graphql' });
  httpServer.on('request', app.callback());
  await new Promise(resolve => httpServer.listen({ port: process.env.PORT }, resolve));
  console.log(
    `External Server ready at http://localhost:${process.env.PORT}${apolloServer.graphqlPath}`
  );

  return { apolloServer, app };
}

server({ typeDefs, resolvers }).then(({ app }) => {
  app.use(cors());
});


The async function serverwill take care of the Koa app itself, and we are going to create the Apollo server with an executable schema where we have to provide types from schema and resolvers. From the official docs, we must call apopServer.start() in advance before apolloServer.applyMiddleware . It allows for identifying potential issues and taking action in the case of crushing the process in Apollo Server startup instead to start serving requests.

The second part is the boilerplate-graphql-koa-server-externallet's set up schema and resolvers.

JavaScript
 
const { gql } = require('apollo-server-koa');

module.exports = gql`
  type Query {
    getItemsExternal: [DataExternalExample]
  }
  type DataExternalExample {
    id: ID
    label: String
  }
  type Mutation {
    updateDataExternal(label: String!): DataExternalExample!
  }
`;


Resolvers for the schema.

JavaScript
 
const fakeData = {
  id: 223421,
  label: 'Some Label From External',
};

module.exports = {
  Query: {
    getItemsExternal: () => [fakeData],
  },
  Mutation: {
    updateDataExternal: (_, { label }) => {
      return {
        ...fakeData,
        label,
      };
    },
  },
};


Now it’s time to check the server responses. Before that, don’t forget to install the following packages: npm i and then run the command npm run start and put in the Chrome browser the URL: http://localhost:4000/api/v1/graphql. Click on the button “Query your server,” and you can get the interface of Apollo GraphQL. It allows you to see the requested schema from the server. Open the Introspection Schema page. You will see there our schema:

Schema

If you were able to introspect the schema, then that means we are done with our boilerplate-graphql-koa-server-external

GraphQL Server for Merging Schemas

Let’s move now to boilerplate-graphql-koa-server setups. Almost everything is the same in package.json from external but with additional packages and different PORT , name, and description.

JSON
 
{
  "name": "boilerplate-graphql-koa-server",
  "description": "Boilerplate GraphQL Koa server",
  "scripts": {
    "start": "PORT=3000 node src/index.js"
  },
  "dependencies": {
    "@graphql-tools/load": "^7.7.5",
    "@graphql-tools/url-loader": "^7.14.1",
  }
}


Let’s setup right away the new schema. There is pretty much the same but a bit different data in the schema.

JavaScript
 
const { gql } = require('apollo-server-koa');

module.exports = gql`
  type Query {
    getFakeDataExample: DataExample
  }
  type DataExample {
    id: ID
    value: String
  }
  type Mutation {
    updateFakeData(value: String!): DataExample!
  }
`;


And resolvers:

JavaScript
 
const fakeData = {
  id: 4838745,
  value: 'Some Random String',
};

module.exports = {
  Query: {
    getFakeDataExample: () => fakeData,
  },
  Mutation: {
    updateFakeData: (_, { value }) => {
      return {
        ...fakeData,
        value,
      };
    },
  },
};


And now, let’s take a look at the server file. You can find out that it’s relatively the same except few lines of code. First of all, we took the loadSchema in order to get the external schema by request from EXTERNAL_ENDPOINT which is our first launched server and the loader for the schema UrlLoader .

The most important that we have to be sure that our schema has been loaded and the external server doesn’t throw any errors. We have to catch that situation. As you can see in the code, we got just an array of schemas. By default, we have only our own internalSchema and then, if an external server is available, we are pushing to that array externalSchema and then use the tool mergeSchemas which helps to provide merged schema right to the ApolloServer

JavaScript
 
const Koa = require('koa');
const http = require('http');
const cors = require('@koa/cors');
const { ApolloServer } = require('apollo-server-koa');
const { loadSchema } = require('@graphql-tools/load');
const { UrlLoader } = require('@graphql-tools/url-loader');
const { makeExecutableSchema, mergeSchemas } = require('@graphql-tools/schema');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');

const EXTERNAL_ENDPOINT = 'http://localhost:4000/api/v1/graphql';

async function server({ typeDefs, resolvers }) {
  const app = new Koa();
  const httpServer = http.createServer();

  const internalSchema = makeExecutableSchema({
    typeDefs,
    resolvers,
  });

  const schemas = [internalSchema];

  try {
    const externalSchema = await loadSchema(EXTERNAL_ENDPOINT, {
      loaders: [new UrlLoader()],
    });
    schemas.push(externalSchema);
  } catch {
    console.warn('⚠️️ External Schema has not been loaded');
  }

  const apolloServer = new ApolloServer({
    introspection: true,
    schema: mergeSchemas({
      schemas,
    }),
  });

  await apolloServer.start();
  apolloServer.applyMiddleware({ app, path: '/api/v1/graphql' });
  httpServer.on('request', app.callback());
  await new Promise(resolve => httpServer.listen({ port: process.env.PORT }, resolve));
  console.log(`Server ready at http://localhost:${process.env.PORT}${apolloServer.graphqlPath}`);

  return { apolloServer, app };
}

server({ typeDefs, resolvers }).then(({ app }) => {
  app.use(cors());
});


Install all packages and run the server, which will be available on the PORT=3000 . Let’s go to the same interface of Apollo GraphQL, but the URL has to be with the proper PORT: http://localhost:3000/api/v1/graphql . Now if we open the Introspection Schema page, we gonna able to see merged schemas. One from external and another one from the last created server.

Introspection Schema View at Apollo GraphQL Studio


Keep in mind that if some of your servers will get the same Field, the GraphQL server will rise the error something like this:

Plain Text
 
Error: Unable to merge GraphQL type “Query”: Field “getFakeDataExample”
already defined with a different type. Declared as “DataExample”,
but you tried to override with “DataExternalExample”


It means that we have to be very careful in a GraphQL schema with our Fields and Type definitions in order to not get into an awkward situation when the Type or Field already exists.

Conclusion

Numerous organizations are adopting a microservice architecture and attempting to isolate the data logic flow. The approach outlined above is particularly useful in situations where microservices communicate with each other within a company. Specifically, when there is a primary global service with a default schema and a secondary microservice with extra fields that may be used by the client in the future, this method allows developers to manage and scale their microservices more efficiently, thereby increasing the overall performance and agility of the system.

GitHub Repos

https://github.com/antonkalik/boilerplate-graphql-koa-server
https://github.com/antonkalik/boilerplate-graphql-koa-server-external

GraphQL Data (computing) Merge (version control) Schema

Published at DZone with permission of Anton Kalik. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Pagination in GraphQL: Efficiently Retrieve and Manipulate Data
  • Why and When to Use GraphQL
  • Consuming GraphQL API With React.js
  • An In-Depth Analysis of GraphQL Functioning Using GenAI Within a Monolithic Application Framework

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: