apollo-server: Apollo Federation: buildFederatedSchema breaks Subscriptions

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",
  },

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 16
  • Comments: 33 (2 by maintainers)

Most upvoted comments

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

Up. We waiting for subscriptions in federation 😃

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

Anything progress on this?

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.

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

Hi, Any proposed release plan for this feature

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!

@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! 😃

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

@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 ?

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?

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

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

@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!

@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

@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

@jbaxleyiii thx u for the answer!

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

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

@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?