Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add createGateway helper function #2911

Conversation

JacksonKearl
Copy link

This is an attempt to implement @glasser's desired API for create gateway. As stated in the comments of AS-105:

import { createGateway } from "@apollo/gateway";
const server = new ApolloServer({
  gateway: await createGateway({ serviceList })
});
console.log(print(server.schema));  // readonly!

Copy link
Contributor

@jbaxleyiii jbaxleyiii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really excited to move towards this API! I think we should wait on this PR until we also add in the needed functionality into AS that makes accepting a gateway possible. It may require larger scale changes and will most certainly be a removal of the existing API so we will need to update the docs.

Can you sync with @glasser and @abernix on next steps for changes internally to AS?

@glasser
Copy link
Member

glasser commented Jun 25, 2019

This is the thing that we had discussed in our conversation @jbaxleyiii . Here are some other brainstormy ideas that avoid the sorta inelegant "apollo-server has to know about gateway and it's not clear what it's doing with that data":

const gateway = await createGateway(...);
const server = gateway.createServer({other, options})

This is bad because it doesn't let you choose which ApolloServer implementation you want. But how about:

new ApolloServer(
  (await createGateway({apiKey})).serverConfig({
    other: ...
    args: ...
  })
)

serverConfig would be something like:

public serverConfig(config: Config) {
  const newConfig = {...config, schema: this.schema, executor: this.executor};
  if ('engine' not in newConfig) {
    newConfig.engine = {apiKey: this.apiKey};
  } else if (typeof newConfig.engine === 'object' && !newConfig.engine.apiKey) {
    newConfig.engine.apiKey = this.apiKey;
  }
  return newConfig;
}

The things I don't like here are:

  • the need for the parens around await (or saving the gateway to a const) — seems easy to write wrong and await the wrong thing
  • serverConfig isn't a great name and I can't think of anything better

@glasser
Copy link
Member

glasser commented Jun 25, 2019

Or @JacksonKearl suggests something like:

new ApolloServer(
  await new ApolloGateway({apiKey}).serverConfig({other, options})
);

where in this case, serverConfig is async and implies running load.

@JacksonKearl
Copy link
Author

JacksonKearl commented Jun 25, 2019

@jbaxleyiii AS can already accept a gateway via new ApolloServer(await createGateway(...)). 3c9bdc5 allows it to also work with new ApolloServer({gateway: await createGateway(...)})

@JacksonKearl
Copy link
Author

JacksonKearl commented Jun 26, 2019

I personally don't see a big issue with passing apiKey twice...

const apiKey = getApiKeyFromSomewhereBesidesTheEnvironment()
const server = new ApolloServer({
   gateway: await createGateway({apiKey}),
   engine: {apiKey},
   playground: process.env === 'STAGING'
})

or

const apiKey = getApiKeyFromSomewhereBesidesTheEnvironment()
const server = new ApolloServer({
   ...await createGateway({apiKey}),
   engine: {apiKey},
   playground: process.env === 'STAGING',
})

or even ditch createGateway entirely:

const apiKey = getApiKeyFromSomewhereBesidesTheEnvironment()
const server = new ApolloServer({
   gateway: await new ApolloGateway({apiKey}).load(),
   engine: {apiKey},
   playground: process.env === 'STAGING'
})

and I wonder how many people aren't defining the apiKey in the environment. Because if it's defined in the environment, it doesnt need to be passed anywhere.

@glasser
Copy link
Member

glasser commented Jun 26, 2019

If we decide it's OK to have people pass apiKey twice if you're not using the env var, then I dunno if any of this refactor is necessary. Unless we anticipate more connections between the packages being added later. I'll trust @jbaxleyiii 's opinion.

@jbaxleyiii
Copy link
Contributor

@glasser @JacksonKearl we will need support for an onSchemaChange handler to be passed from the gateway to ApolloServer for changes to the schema resulting from new composition. So { schema, executor, plugins?, onSchemaChange } already feels like a lot to destructure then pass. I personally don't have a problem with the gateway addition to AS (we originally called it service) which would prevent the need to pass apiKey twice if we merge the options.

@JacksonKearl
Copy link
Author

@jbaxleyiii one caveat is that you never actually need to destructure then pass. You can just pass the return value. This is made more formal by having the gateway creator return an interface and the server constructor accepting that interface.

@glasser
Copy link
Member

glasser commented Jun 26, 2019

I find "we give it to you as an object of a class with methods but you're expected to use it by destructuring it" to be quite weird. I don't even feel like I know well as a developer on this project exactly which fields/methods from a class are going to "show up" when destructured and what won't. It's one thing to say "load() returns a plain old JS object with some keys, use it by destructuring because the keys may change"... that seems more understandable than "it returns an ApolloGateway class instance but you can destructure it".

@@ -261,6 +263,19 @@ export class ApolloServerBase {
throw new Error(errors.map(error => error.message).join('\n\n'));
}
this.schema = schema!;
} else if (gateway) {
this.schema = gateway.schema;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a throw here if subscriptions are currently enabled? While there's an unlikely chance that some aspect of the (bolted on, but entirely separate) subscription server might startup and run right now with the current federation implementation, we're even farther from being able to support that when schema reloading comes into the picture.

While we're going to want to fix that, that's unlikely to be before Apollo Server 3 which hopes to fix subscriptions (See #2360).

@@ -77,7 +84,7 @@ export interface Config extends BaseConfig {
introspection?: boolean;
mocks?: boolean | IMocks;
mockEntireSchema?: boolean;
engine?: boolean | EngineReportingOptions<Context>;
engine?: false | EngineReportingOptions<Context>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just true is still a valid option, no?

@JacksonKearl
Copy link
Author

Just going to merge this into the staging branch now because it is getting to be a big hassle to tie all these things together while everything is still partially implemented on separate branches.

@JacksonKearl JacksonKearl merged commit 37ca5f5 into enterprise-merge-megabranch Jun 27, 2019
@abernix abernix deleted the enterprise-merge/createGateway-interface branch June 28, 2019 07:28
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants