Apollo federation with gateway server as service

Viewed 2768

The summary of the issue has been described in a Github issue:

https://github.com/apollographql/apollo-server/issues/2794


In short, is there a way to implement Apollo Federation such that the gateway itself has it's own schema?

Federating Apollo Server where the gateway itself has a schema

@apollo/gateway@0.6.5 @apollo/federation@0.6.2

Expected Behavior

When I am instantiating my Apollo Gateway server, I expect that I should be able to merge schemas from federated services, as well as the schema from the gateway itself.

Actual Behavior

The gateway server fails to mount the /graphql route because it is expecting all of the services to be currently running before it does so.

On run time, the following error is printed to the console:

POST /graphql 404 11.251 ms - 147
Encountered error when loading gateway-app at http://localhost:8000/graphql: invalid json response body at http://localhost:8000/graphql reason: Unexpected token < in JSON at position 0
[DEBUG] Fri Jun 07 2019 12:11:07 GMT-0400 (Eastern Daylight Time) apollo-gateway: Configuration loaded for Gateway
[DEBUG] Fri Jun 07 2019 12:11:07 GMT-0400 (Eastern Daylight Time) apollo-gateway: Composing schema from service list:
  remove-svc
TypeError: schema.toConfig is not a function
    at Object.composeServices (/gateway-app/node_modules/@apollo/federation/dist/composition/compose.js:191:67)
    at Object.composeAndValidate (/gateway-app/node_modules/@apollo/federation/dist/composition/composeAndValidate.js:13:41)
    at ApolloGateway.createSchema (/gateway-app/node_modules/@apollo/gateway/dist/index.js:90:47)
    at ApolloGateway.<anonymous> (/gateway-app/node_modules/@apollo/gateway/dist/index.js:81:22)
    at Generator.next (<anonymous>)
    at fulfilled (/gateway-app/node_modules/@apollo/gateway/dist/index.js:4:58)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Source Code

const url = new URL(`redis://${REDIS_URL}:${REDIS_PORT}/${REDIS_DATABASE}`).toString()

const cache = new RedisCache({ url })

const context = ({ req }) => {
  if (!(req.user || req.headers.authorization === ROUTE_AUTH)) {
    throw new AuthenticationError('Not authenticated')
  }
  return { user: req.user, req: req }
}

try {
  const loadGateway = async () => {
    try {
      const { schema, executor } = await gateway.load()
      return { schema, executor }
    } catch (err) {
      console.error(err)
      return null
    }
  }

  const gateway = new ApolloGateway({
    debug: process.env.ENV !== 'prod',
    serviceList: [
      { name: 'gateway', url: `${GATEWAY_HOSTNAME}/graphql` },
      { name: 'remote-svc', url: `${REMOTE_SERVICE_HOSTNAME}/graphql` },
    ],
  })

  const { schema, executor } = loadGateway()
  const server = new ApolloServer({
    schema,
    executor,
    cache,
    dataSources,
    engine: { apiKey: ENGINE_API_KEY },
    tracing: true,
    context,
  })
  server.applyMiddleware({ app })
} catch (err) {
  console.error(err)
}
1 Answers

Based on the original errors reported by my issue, there are two bugs.

  1. An Apollo Gateway server cannot and should not also be a federated service with its own schema (see Github Issue #2792).

From Apollo:

I'll jump to a conclusion here based on the naming of your service: are you trying to run the gateway at port 8000 and treat it as a federated service at the same time? If so, that's not going to work; the gateway has to resolve all schemas before it can compose the final schema.

  1. GraphQL core dependency must be at least at version 14.2.0 (see Github Issue #2825).

From GraphQL JS Changelogs:

toConfig is intended to help with schema transformation by minimizing the amount of code you need write and maintain, e.g. recreateType from graphql-tools.

When I worked on graphql-voyager I created my own intermidiate format just to do schema transformations.

Moreover, toConfig will prevent bugs that occur when a new field is added. For example, lexicographicSortSchema doesn't respect newly added assumeValid and allowedLegacyNames fields.

After migrating the schema from the gateway to its own federated service and updating my core GraphQL dependency to the appropriate version, I was succesfully able to implement the gateway/federation pattern.

Related