DEV Community

Cover image for Simple and to the point stitching remote schema into local schema with Apollo Server
Rodrigo Figueroa
Rodrigo Figueroa

Posted on

Simple and to the point stitching remote schema into local schema with Apollo Server

Cover photo by Two Paddles Axe and Leatherwork on Unsplash

Scenario: You have your first schema running on your Apollo server but a new GraphQL API shows up and you need to work with it in your client right away without too much orquestation.

This tutorial will try to get you from point A to point B as fast as possible. We will make a small API to solve our problem and for future reuse. You can use it right away but we will be explaining each piece step by step to get the inner workings.

A note:
Stitching local with remote schemas, I think, works perfectly in the situation where you are starting to stitch. Why? Because you have your first schema already up and running. If a GraphQL service shows up just append this new remote schema on top to start consuming it.

I'm telling you this because there is another way of working this out AFAIK but we would need to start another microservice to serve as a proxy to both of our APIs (local and remote schemas will be both remote now). I think this could work well when you have too many services but for a start let's just use our current server and enhance it with a new remote schema on top of it. It's faster, it's one less service to run, it works.

Right now supposedly we have this setup for our server.

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

server.listen().then(({ url }) => {
  console.log(`πŸš€ Server ready at ${url}`);
});
Enter fullscreen mode Exit fullscreen mode

We are going to replace previous code with this module.

const { ApolloServer, makeExecutableSchema } = require('apollo-server')
const { HttpLink } = require('apollo-link-http')
const {
  introspectSchema,
  makeRemoteExecutableSchema,
  mergeSchemas,
} = require('graphql-tools')
const fetch = require('node-fetch')

async function startServer({
  localSchema: { resolvers, typeDefs },
  remoteSchema: { uri },
}) {
  const remoteHttpLink = new HttpLink({
    uri,
    fetch,
  })

  const remoteSchemaInstrospection = await introspectSchema(remoteHttpLink)

  const remoteSchemaExecutable = makeRemoteExecutableSchema({
    schema: remoteSchemaInstrospection,
    link: remoteHttpLink,
  })

  const localSchema = makeExecutableSchema({
    typeDefs,
    resolvers,
  })

  const mergedSchemas = mergeSchemas({
    schemas: [localSchema, remoteSchemaExecutable],
  })

  const server = new ApolloServer({ schema: mergedSchemas })

  return await server.listen()
}

module.exports = startServer
Enter fullscreen mode Exit fullscreen mode

First we are going to make a request to our external GraphQL API. We pass to the config object our uri and a reference to the fetch library.

  const remoteHttpLink = new HttpLink({
    uri,
    fetch,
  })
Enter fullscreen mode Exit fullscreen mode

We use the instrospectionSchema function to retrive the schema from our remote GraphQL service. Only argument is our previously defined HttpLink instance.

  const remoteSchemaInstrospection = await introspectSchema(remoteHttpLink)
Enter fullscreen mode Exit fullscreen mode

We got our schema after introspection but now we are still missing making it executable. So it's a two step process. Being executable will enables us to later on merge it with our local one.

  const remoteSchemaExecutable = makeRemoteExecutableSchema({
    schema: remoteSchemaInstrospection,
    link: remoteHttpLink,
  })
Enter fullscreen mode Exit fullscreen mode

Next we create our local schema using makeExecutableSchema passing a config object as argument with our typeDefs and resolvers just like the typical server config object we saw at the beginning.

const localSchema = makeExecutableSchema({
  typeDefs,
  resolvers,
})
Enter fullscreen mode Exit fullscreen mode

Merge both schemas with mergeSchemas

const mergedSchemas = mergeSchemas({
  schemas: [localSchema, remoteSchemaExecutable],
})
Enter fullscreen mode Exit fullscreen mode

Pass it to our server in its config object and start it invoking listen as usual.

const server = new ApolloServer({ schema: mergedSchemas })

return await server.listen()
Enter fullscreen mode Exit fullscreen mode

We are done with the module breakdown πŸ™Œ. Now let's use it!

startServer has as argument one config object that has two properties:

  • localSchema Object with two keys. resolvers and typeDefs.
  • remoteSchema Object with one key.uri: string of our external GraphQL API.
startServer({
  localSchema: {
    resolvers,
    typeDefs,
  },
  remoteSchema: {
    uri: 'https://01uhb.sse.codesandbox.io/',
  },
}).then(({ url }) => {
  console.log(`πŸš€  => Server ready at ${url}`)
})
Enter fullscreen mode Exit fullscreen mode

And that's it. Now our current server has both of our schemas available.

Play with it yourself. Here's a codesandbox with the GraphQL server and our module in action. We have two queries. hello is local and the goodbye query is being merge.

You can grab the external Graphql service here: https://codesandbox.io/s/apollo-server-01uhb

This is just the beginning. You have a lot to tweak and learn about stitching from here.

What if some types repeat between schemas?
What if I need to use the result type of one query in another one as an argument?

New challenges bring new ways to extend and work with combined schemas.
Hope this tut is a good first step into your stitching journey!

Top comments (0)