Netflix / dgs-federation-example

Apache License 2.0
108 stars 43 forks source link

Production Ready Apollo Gateway for GraphQL Services issue #10

Open jay-korsi opened 3 years ago

jay-korsi commented 3 years ago

Hi Team,

We are new to GraphQL and we built couple of GraphQl Services using Netflix DGS.

Now we would like to federate those services into single endpoint and started working on it by cloning the apollo gateway federation by netflix example dgs-federation-example/apollo-gateway at master · Netflix/dgs-federation-example · GitHub.

This works perfectly fine on my local machine. But When we deploy the node js app to our AWS EKS env, I am not sure how can I access the apollo server. I always see the Server ready at http://localhost:4000/

I have exposed the port 4000 as well but no use.

Here is my code for creating the apollo server. Please help. I have been trying it for couple of weeks now. I didn’t find a production ready federation example also.

'use strict'

const https = require('https')
const http = require('http')
const config = require('config')
const app = require('./app.js')
const log = require('<custom_logger>')
const { ApolloServer } = require('apollo-server')
const { ApolloGateway, RemoteGraphQLDataSource } = require('@apollo/gateway')
var os = require("os");

var hostname = os.hostname();
console.log("hostname:"+hostname)
let httpListenerPort = 8080
let httpsListenerPort = 8443

log.info('NODE_ENV: ' + config.util.getEnv('NODE_ENV'))
log.info('NODE_APP_INSTANCE: ' + config.util.getEnv('NODE_APP_INSTANCE'))
log.info('NODE_CONFIG_DIR: ' + config.util.getEnv('NODE_CONFIG_DIR'))
log.levels(
  0,
  process.env.LOG_LEVEL
    ? parseInt(process.env.LOG_LEVEL, 10)
    : 'info'
)

log.info('log.levels(): ' + log.levels())

const gateway = new ApolloGateway({
  serviceList: [
    {
      name: 'service1',
      url: '<link_to_service1>/graphql'
    },
    {
      name: 'service2',
      url: '<link_to_service2>/graphql'
    },
    {
      name: 'service3',
      url: '<link_to_service3>/graphql'
    },
    {
      name: 'service4',
      url: '<link_to_service4>/graphql'
    }
  ],
  introspectionHeaders: {
    apikey: process.env.API_KEY
  },
  buildService ({ url }) {
    return new RemoteGraphQLDataSource({
      url,
      willSendRequest ({ request }) {
        request.http.headers.set('apikey', process.env.API_KEY)
      }
    })
  }
})

const httpServer = http.createServer(app).listen(httpListenerPort, () => {
  log.info('app is listening at localhost:' + httpListenerPort)
})

const httpsServer = https
  .createServer(<api_certs>, app)
  .listen(httpsListenerPort, () => {
    log.info('app is listening at localhost:' + httpsListenerPort)
  })

const server = new ApolloServer({
  gateway,
  subscriptions: false,
  tracing: true,
  introspection: true, // TO ENABLE PLAYGROUND IN PRODUCTION
  playground: true, // TO ENABLE PLAYGROUND IN PRODUCTION
  onHealthCheck: () => {
    return new Promise(resolve => {
      resolve()
    })
  }
})

server.listen(4000).then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`)
  console.log(
    `Try your health check at: ${url}.well-known/apollo/server-health`,
  );
}).catch(err => {console.error(err)});

function timeoutExit (seconds = 5) {
  const timeout = setTimeout(() => {
    log.fatal(
      { exitCode: process.exitCode },
      `Application did not gracefully shutdown in ${seconds} seconds. Forcibly terminating.`
    )
    process.exit()
  }, seconds * 1000)
  // un-ref the timer so Node.js exits even with this running timer
  timeout.unref()
  return timeout
}

function closeServers () {
  httpServer.close(() => {
    log.info('HTTP server is shutdown')
  })
  httpsServer.close(() => {
    log.info('HTTPS server is shutdown')
  })
}

process.on('SIGTERM', () => {
  log.info('SIGTERM issued...app is shutting down')
  closeServers()
  timeoutExit()
})

process.on('uncaughtException', error => {
  log.fatal(
    error,
    'Uncaught Exception! The server is now in an unrecoverable state. Terminating...'
  )
  closeServers()
  // the app should exit on its own when there are no daemons running nor events on the event loop

  // forcibly terminate if haven't done so gracefully within timeout
  timeoutExit()
})

process.on('unhandledRejection', (reason, promise) => {
  log.error(
    {
      err: reason,
      promise
    },
    'Unhandled Promise Rejection'
  )
})

Not having any other configs for playground too. Have health check for http server only on top of it. Please suggest on how to spin the apollo server up and running in our K8s environment

jonckvanderkogel commented 3 years ago

You can find the answer here. In essence, if you want to deploy locally you have to create a listing of your services, if you want to deploy using Apollo Studio you should remove those service references in the constructor and rather use Rover CLI to register your services to Apollo Studio. Your gateway will then automatically discover them.