Rover supergraph routing_url per env

Can anyone advise on best practice for running Apollo Gateway with a rover generated supergraph in multiple environments? Locally, the routing_url for each subgraph is localhost:port, but in prod/non-prod envs, it will need to be something else.

For context, we’re building containers in CI, to be deployed to K8s clusters; the same container used in both prod and non prod. We can inject environment variables easily enough, but the supergraph is hardcoded to whatever URLs are in the config.yaml at the time of build.

How have others approached this?

I could see a couple few options:

  • Have your container with the Gateway rely on the supergraph being at a known location in the container (e.g., /supergraph.graphql) and externalize the creation of that supergraph (via rover) to another step which happens before launching the container. For example, using Kubernetes ConfigMaps to mount the generated artifact (or possibly using Docker’s -v volume flags if developers are using Docker to spawn the stack locally?)

  • If there’s a finite set of variations, just generate all of them at the container build time (e.g., generate a supergraph.production.graphql, supergraph.staging.graphql and supergraph.localhost.graphql) at container build time, then use environment variables in the ApolloGateway constructor to toggle between them (e.g., SUPERGRAPH_FILE=supergraph.staging.graphql and using new ApolloGateway({ supergraphSdl: readFile(process.env.SUPERGRAPH_FILE) }).

  • Use the same supergraph for all of them, but merely change the URLs using an implementation of the buildService function to the ApolloGateway constructor. This function is called for each subgraph (e.g., products, reviews, etc.). It requires a RemoteGraphQLDataSource implementation be returned from it and the RemoteGraphQLDataSource is ultimately the source of truth for communicating to the subgraph, so you can override it at this point — possibly even using environment variables made available to Kubernetes pods by K8s service discovery. For example, if you have reliable conventions for your k8s Service subgraphs:

    import { ApolloGateway, RemoteGraphQLDataSource } from 'apollo-gateway';
    const gateway = new ApolloGateway({
      supergraphSdl: readFileSync('/supergraph'),
      buildService({ name, url }) {
        const urlToReallyUse =
          'http://' +
          process.env[`${name.toUpperCase()}_SERVICE_HOST`] +
          process.env[`${name.toUpperCase()}_SERVICE_PORT`];
        return new RemoteGraphQLDataSource({ url: urlToReallyUse });
      },
    });
    

    … of course, don’t trust this code exactly as it is. I’ve mostly just written it here on the spot. :wink:

Hope some of these suggestions help! Let me know what you think.

Thanks for this! Much appreciated. We ended up taking the second route, as K8s lets us hit services by service name, like docker does. So now we have a local and prod supergraph config that gets built depending on environment. I will pass this onto the team though in case someone prefers any of these alternatives.