apollographql / apollo-server

🌍  Spec-compliant and production ready JavaScript GraphQL server that lets you develop in a schema-first way. Built for Express, Connect, Hapi, Koa, and more.
https://www.apollographql.com/docs/apollo-server/
MIT License
13.75k stars 2.03k forks source link

Apollo Federation: buildFederatedSchema breaks Subscriptions #2776

Closed Max-Starling closed 5 years ago

Max-Starling commented 5 years ago

Intro

First of all I want to say that I really like the Apollo Federation as an idea. And it works well now for all Queries and Mutations. Unfortunately, this isn't the case for Subscriptions.

Problem

When I use buildFederatedSchema for ApolloServer config I get the Network Error on the client side: image

Interestingly, the same code works well with mergeTypes and mergeResolvers.

As I understand there is a server side problem. And I will explain this point below.

Description

I will show you two solutions. Working and not working ones. For simplicity, I will omit the splitting into files and unnecessary pieces of code.

Common code parts:

/* app.js */

const GraphQLConfig = require('./graphql.config');

const app = new Koa();

const apolloServer = new ApolloServer(GraphQLConfig);
apolloServer.applyMiddleware({
  app,
  path: '/graphql',
});

app.listen({ port: config.port }, () => { /* nothing important here */ });

const WS_PORT = 5000;
const websocketServer = createServer(app);

apolloServer.installSubscriptionHandlers(websocketServer);

websocketServer.listen(WS_PORT, () => { /* nothing important here */ });
/* graphql.config.js */

const gql = require('apollo-server-koa').gql;
const PubSub = require('graphql-subscriptions').PubSub;

const messageSchema = {};

messageSchema.typeDefs = gql`
  type Subscription {
    messageAdded: Message
  }

  type Message {
    text: String!
  }

  type Mutation {
    createMessage (text: String!) : Message
  }
`;

const pubsub = new PubSub();
const MESSAGE_ADDED = 'MESSAGE_ADDED';

messageSchema.resolvers = ({
  Subscription: {
    messageAdded: {
      subscribe: () => pubsub.asyncIterator(MESSAGE_ADDED),
    }
  },
  Mutation: {
    createMessage: () => {
      const message = ({ text: 'I must be sent.' });
      pubsub.publish(MESSAGE_ADDED, { messageAdded: message });
      return message;
    },
  },
});

module.exports = ({
  /* (typeDefs & resolvers) or schema */
  context: () => { /* ... */ },
  uploads: false,
  subscriptions: {
    path: '/graphql',
    onConnect: () => { /* ... */ },
    onDisconnect: () => { /* ... */ },
  },
  formatError: (err) => {
    console.log(err);
  },
});

Working case:

  const mergeTypes = require('merge-graphql-schemas').mergeTypes;
  const mergeResolvers = require('merge-graphql-schemas').mergeResolvers;

  /* code above */

 const typeDefs =  mergeTypes([
    MessageSchema.typeDefs,
   /* ... */
  ], { all: true });

 const resolvers =  mergeResolvers([
    MessageSchema.resolvers,
    /* ... */
 ]);

module.exports = ({
  typeDefs,
  resolvers,
  /* ... */
});

Not working one:

 const buildFederatedSchema = require('@apollo/federation').buildFederatedSchema;

  /* ... */

 const schema = buildFederatedSchema([
    MessageSchema,
    /* ... */
 ]);

module.exports = ({
  schema,
  /* ... */
});

Config's formatError doesn't catch any error here. But on the client side I can see Network Error by using this code:

 import { onError } from 'apollo-link-error';

 const link = /* ... */

 const errorLink = onError(({ graphQLErrors, networkError }) => {
   if (graphQLErrors) /* ... */
   if (networkError) {
     console.log('[Network error]', networkError);
   };
 }

 const client = new ApolloClient({
    link:  errorLink.concat(link),
 });
});

However, I managed to find the file in node_modules which throwing the error:

/* server/node_modules/graphql/subscription/subscribe.js */

/* ... */

function createSourceEventStream(schema, /* ... */) {
 const fieldDef = /* some actions with schema */;
  /* ... */
 const eventStream = /* some actions with fieldDef */
 /* ... */
 throw new Error('Subscription field must return Async Iterable. Received: ' + inspect(eventStream));
}

I tried to display the contents of the fieldDef on the console and it hasn't subscribe field, so eventStream variable becomes undefined here! So network error is expected.

image

But where is my subscribe method? I know, that it's defined well because:

console.log('I)', typeof resolvers.Subscription.messageAdded.subscribe);
console.log('II)', resolvers.Subscription.messageAdded.subscribe());

image

Packages (server):

 "dependencies": {
    "@apollo/federation": "^0.6.0",
    "apollo-server-koa": "^2.6.1",
    "graphql": "^14.3.1",
    "graphql-subscriptions": "^1.1.0",
    "koa": "^2.7.0",
    "merge-graphql-schemas": "^1.5.8",
    "subscriptions-transport-ws": "^0.9.16",
  },

Packages (client):

 "dependencies": {
    "apollo-boost": "^0.3.1",
    "apollo-cache-inmemory": "^1.6.0",
    "apollo-client": "^2.6.0-rc.3",
    "apollo-link": "^1.2.11",
    "apollo-link-http": "^1.5.14",
    "apollo-link-ws": "^1.0.17",
    "apollo-utilities": "^1.3.0",
    "graphql": "^14.3.1",
    "graphql-tag": "^2.10.1",
    "graphql-tools": "^4.0.4",
    "react": "^16.8.6",
    "react-apollo": "^2.5.6",
    "subscriptions-transport-ws": "^0.9.16",
  },
Pruxis commented 5 years ago

In the release blogpost at the bottom they stated this: Support for subscriptions and @defer

Source: https://blog.apollographql.com/apollo-federation-f260cf525d21

Max-Starling commented 5 years ago

@Pruxis Sorry, I read the Apollo Docs only. I think guys can add at least a mention there to avoid misunderstandings later. Subscriptions is quite an important thing to post it only in the blogpost. In any case, looking forward to implementation. Thanks! :)

var commented 5 years ago

how far down the roadmap are subscriptions? we'd like to use federation but no subscriptions is a deal breaker

var commented 5 years ago

We are about to refactor the backend of an app and would like to use federation, but subscriptions are extremely important as well. I was wondering what are the community's thoughts on building a hybrid of federation + schema stitching and getting rid of schema stitching layer once subscriptions are supported with federation. Something like this -

Before subscriptions support in federation:

Screen Shot 2019-06-18 at 8 28 04 PM

After subscriptions support in federation:

Screen Shot 2019-06-18 at 8 44 17 PM

I did get this to work and I don't see a reason why it wouldn't. But other than the redundancy of gateway and schema stitching, are there any serious drawbacks to this?

Anyone reading this.. please do not implement this without knowing what you are doing.

herenickname commented 5 years ago

Up. We waiting for subscriptions in federation :)

standuprey commented 5 years ago

Yes, and it looks simple to add. Just change

      if (typeof fieldConfig === "function") {
        field.resolve = fieldConfig;
      } else {
        field.resolve = fieldConfig.resolve;
      }

to

      if (typeof fieldConfig === "function") {
        field.resolve = fieldConfig;
      } else {
        field.resolve = fieldConfig.resolve;
        field.subscribe = fieldConfig.subscribe;
      }

in buildSchemaFromSDL. Unfortunately this is part of apollo-graphql and the source is not on Github, so no PR, or am I missing something?

jbaxleyiii commented 5 years ago

@Max-Starling this is definitely on our near term roadmap for both Apollo Server (#2360) and federation all together! I'm going to close this issue since it is part of our roadmap!

Max-Starling commented 5 years ago

@jbaxleyiii thx u for the answer!

mm7mod commented 5 years ago

is there an estimate for the new version release date that will support subscriptions with federation ? my app relies heavily on subscriptions but I want to use federations as my schema is getting big

valdestron commented 4 years ago

I implemented @var approach with two gateways, the only thing that I am struggling right now is to find a good approach to pass the context from the stitcher gateway to the upstream subscription service.

Any Ideas how should i link the context from gateway to subscription service, or atleast get the authorization header in the upstream service ?

The best what I achieved is: https://www.apollographql.com/docs/graphql-subscriptions/authentication/ Validating auth token in the onConnect lifecycle hook in the gateway, although this is kinda ok, best approach would to handle this in my upstream service.

KoltesDigital commented 4 years ago

I'm also waiting for the feature, and I think @valdestron's question is relevant. Quickly looking at https://github.com/apollographql/apollo-server/blob/650f298617399c4d8cd06c109a53948930815bba/packages/apollo-gateway/src/executeQueryPlan.ts#L288 and https://github.com/apollographql/apollo-server/blob/650f298617399c4d8cd06c109a53948930815bba/packages/apollo-gateway/src/datasources/RemoteGraphQLDataSource.ts#L55 shows that headers are not passed to the services. I hope this will be possible.

riginoommen commented 4 years ago

Hi, Any proposed release plan for this feature

antiqe commented 4 years ago

Hi @jbaxleyiii, i know it's an old topic but did you have an estimate ETA regarding the support of subscription with federation ? Thanks a lot

tomerzcod7 commented 4 years ago

Hey guys, is there a solid alternative approach to subscriptions when dealing with GraphQL microservices architecture as long as subscriptions are not supported by Apollo Federation? We were about to start using it for our distributed graph and then we have found out about this issue. Can't seem to find any recommended alternatives or information.

riginoommen commented 4 years ago

Hey guys, is there a solid alternative approach to subscriptions when dealing with GraphQL microservices architecture as long as subscriptions are not supported by Apollo Federation? We were about to start using it for our distributed graph and then we have found out about this issue. Can't seem to find any recommended alternatives or information.

Hi @tomerzcod7 ,

Can we try this approach. https://github.com/apollographql/apollo-server/issues/2776#issuecomment-503361983

tomerzcod7 commented 4 years ago

Hey guys, is there a solid alternative approach to subscriptions when dealing with GraphQL microservices architecture as long as subscriptions are not supported by Apollo Federation? We were about to start using it for our distributed graph and then we have found out about this issue. Can't seem to find any recommended alternatives or information.

Hi @tomerzcod7 ,

Can we try this approach. #2776 (comment)

It seems like @var was also asking what is the community's feedback about this as a solution. I was just wondering if since then this approach was recommended for this use case?

I was also wondering if you had to rethink or even refactor services so only a few of them would actually implement subscriptions? Otherwise, in my case, most of the services won't work with the gateway at all.

yaacovCR commented 4 years ago

@tomerzcod7 the alternative is schema stitching, which has supported subscriptions for some time.

Schema stitching has been enhanced in graphql-tools v6 with crucial bug fixes and initial support for type merging.

See https://www.graphql-tools.com/docs/schema-stitching#merging-types and https://the-guild.dev/blog/graphql-tools-v6

valdestron commented 4 years ago

@tomerzcod7 the alternative is schema stitching, which has supported subscriptions for some time.

Schema stitching has been enhanced in graphql-tools v6 with crucial bug fixes and initial support for type merging.

See https://www.graphql-tools.com/docs/schema-stitching#merging-types and https://the-guild.dev/blog/graphql-tools-v6

I am a bit confused, are you saying that we should now migrate from apollo fedaration again to schema stitching ? I was actually using both, apollo federation is superrior and declerative way of defining upstream schemas. People here is trying to solve a problem with apollo federation and subscriptions which is as far as I know not solved, what we want is to completely get rid of schema stitching. I remember the end date of Apollo 3.0 was 2020 April now is July and we still have the same issue with subscriptions. Correct me if I am wrong ?

bilby91 commented 4 years ago

@yaacovCR Is it possible to use Apollo Federation in convention with Schema Stiching just for subscription ?

Thanks!

valdestron commented 4 years ago

@yaacovCR Is it possible to use Apollo Federation in convention with Schema Stiching just for subscription ?

Thanks!

Yes it is possible, I have implemented such an approach that descirbed @var it works. But there is few ugly issues:

bilby91 commented 4 years ago

@valdestron Appreciate you taking the time to provide feedback! That is really useful!

Question, is it possible to return an external type from a subscription and expect the gateway to resolve the type ?

For example, I have a subscription userCreated that returns a UserType. The UserType is owned by one of the federated services. Will the gateway be smart enough to resolve it ?

Thanks again!

valdestron commented 4 years ago

@valdestron Appreciate you taking the time to provide feedback! That is really useful!

Question, is it possible to return an external type from a subscription and expect the gateway to resolve the type ?

For example, I have a subscription userCreated that returns a UserType. The UserType is owned by one of the federated services. Will the gateway be smart enough to resolve it ?

Thanks again!

Yes its possible to do that. You will have to tinker around the remote schema combining and merging mechanisms in the gateway, there is also some tools available for types.

pranaypratyush commented 3 years ago

Anything progress on this?

fabis94 commented 3 years ago

I'd like to hear any updates regarding this as well. Federation not supporting subscriptions is the main reason why we don't plan to use it for the time being where I work

sam3d commented 3 years ago

Hi! My team and I are looking at integrating Apollo federation into our public utility-scale GraphQL layer. Right now subscription support is the only thing preventing us from being able to do this. Any updates from the Apollo team would be super handy!

Spolja commented 3 years ago

@yaacovCR Is it possible to use Apollo Federation in convention with Schema Stiching just for subscription ? Thanks!

Yes it is possible, I have implemented such an approach that descirbed @var it works. But there is few ugly issues:

  • subscription services are so decoupled that you are unable to share context
  • you are unable to use same auth mechanisms that you are using in federated services, this forces you to reimplement auth mechanism in each subscription service
  • basically what you have is like this: gql subs

@valdestron I'm interested how did you manage to stitch non federated services into federated gateway, do you have some sample code?

valdestron commented 3 years ago

@yaacovCR Is it possible to use Apollo Federation in convention with Schema Stiching just for subscription ? Thanks!

Yes it is possible, I have implemented such an approach that descirbed @var it works. But there is few ugly issues:

  • subscription services are so decoupled that you are unable to share context
  • you are unable to use same auth mechanisms that you are using in federated services, this forces you to reimplement auth mechanism in each subscription service
  • basically what you have is like this: gql subs

@valdestron I'm interested how did you manage to stitch non federated services into federated gateway, do you have some sample code?

I have stitcher and federation gateways. Stitcher gateway on top it stitches graphql schema from upstream federation gateway, federation gateway federates schemas from upstream services. Stitcher gateway also introspects subscription service endpoint that later is merged with federation gateway introspected schema.

Its just schema handling, in federated gateway everything is according to the docs. In stitched gateway you have two endpoints that you introspect federated gw and subscription, basically you get raw schemas from both endpoints then transform and merge them.

I suggest migrate to fastify + mercurius. You do not even need to migrate the apollo federated services only gateway.

valdestron commented 3 years ago

I created a new project ecommerce-blueprint where I migrated from apollo-gateway to fastify but used apollo-federation for schema. Not because i dont like apollo-gateway, just because I have no other choice. If Apollo rise up and start delivering the 3.0 with the must have features, I will definetly come back.

For anyone interested how I implemented federation, you can find source code here https://github.com/sagahead-io/ecommerce-blueprint

riginoommen commented 3 years ago

HI ,

With the last summit there is a solution to use the subscriptions with the federated schema. I request everyone to go through this.

Talk - https://www.apollographql.com/graphql-summit/thank-you/using-subscriptions-with-your-federated-data-graph

Example Code - https://github.com/apollographql/federation-subscription-tools

Regards Rigin Oommen

valdestron commented 3 years ago

HI ,

With the last summit there is a solution to use the subscriptions with the federated schema. I request everyone to go through this.

Talk - https://www.apollographql.com/graphql-summit/thank-you/using-subscriptions-with-your-federated-data-graph

Example Code - https://github.com/apollographql/federation-subscription-tools

Regards Rigin Oommen

Is this temporary aproach until some major release ?

timonmasberg commented 2 years ago

HI , With the last summit there is a solution to use the subscriptions with the federated schema. I request everyone to go through this. Talk - https://www.apollographql.com/graphql-summit/thank-you/using-subscriptions-with-your-federated-data-graph Example Code - https://github.com/apollographql/federation-subscription-tools Regards Rigin Oommen

Is this temporary aproach until some major release ?

It feels like this is their current “go-to” solution being promoted in several channels. I think this is kind of ugly due to having 2 entry points in your cluster and the need for auth and context duplication.

riginoommen commented 2 years ago

HI , With the last summit there is a solution to use the subscriptions with the federated schema. I request everyone to go through this. Talk - https://www.apollographql.com/graphql-summit/thank-you/using-subscriptions-with-your-federated-data-graph Example Code - https://github.com/apollographql/federation-subscription-tools Regards Rigin Oommen

Is this temporary aproach until some major release ?

Still I am not sure

sammysaglam commented 2 years ago

having waited for subscriptions support for a while now ⏳ , i was instead able to put together a small library implementing federation with subscriptions (the way we'd hoped for, without a separate service just for subscriptions). was able to get it working using schema-stitching & graphql-tools, it's basically just transforming federation SDL into schema-stitching SDL and using websockets across the gateway & subgraphs: https://github.com/sammysaglam/federation-with-subscriptions

hope that helps