federation icon indicating copy to clipboard operation
federation copied to clipboard

Gateway throws error when federated schema contains Subscription type

Open jsangilve opened this issue 4 years ago • 11 comments

  • Package: @apollo/gateway (but the error is caused by @apollo/federation)
  • Version: since @apollo/federation v0.9.1
  • Last known working version: v0.6.x

When a federated service's schema contains a Subscription type and it's loaded by the Apollo Gateway, the following error is returned:

GraphQLSchemaValidationError: [accounts] Subscription -> `Subscription` is an extension type, but `Subscription` is not defined

I understand Subscriptions aren't supported by Federation yet, but having them in the downstream service shouldn't affect the creation of the federated graph.

While debugging the problem, I realized the Gateway was processing the Subscription as an extension type (the same way it does with Query/Mutation), but it's not adding an empty definition type for Subscription.

To reproduce it, just run the federation demo adding a Subscription to the accounts service:

const typeDefs = gql`
  extend type Query {
    me: User
  }

  type User @key(fields: "id") {
    id: ID!
    name: String
    username: String
  }

  type Subscription {
    somethingHappened(input: String!): String!
  }
`;

Fork including the change above: https://github.com/jsangilve/federation-demo/blob/wip/example_with_subscription/services/accounts/index.js

jsangilve avatar Sep 30 '19 13:09 jsangilve

Thanks for opening apollographql/apollo-server#3358, but I've closed it until we can discuss it a bit more. I'm curious — given the fact that subscriptions aren't supported by Apollo Gateway — if you can provide more details on the use-case where this does more than move failure from one point to another.

Elaborating: Execution of a graph with subscriptions in it is still going to be problematic, and we don't suggest exposing downstream services on their own so it feels like this just moves a failure from an earlier point to a later point, which seems like it sets false expectations for a developer which might result in disappointment after they've already put time into it.

abernix avatar Oct 03 '19 12:10 abernix

Hey @abernix, thanks for taking the time to review the PR and look into this issue.

We have been using Apollo Federation since version 0.6.x. Our architecture is composed by different micro-services exposing GraphQL APIs; these APIs are composed together by the Apollo Gateway.

That's our basic setup — probably the most common between teams adopting Apollo Federation — and it worked, until we needed to provide real-time updates to our clients. We liked the idea of providing clients with all the info they needed through a single GQL Schema. However, given that Apollo Gateway does not support subscriptions, we looked for alternatives.

There are many ways, but I wrote down 4 different ways to overcome this issue:

  1. Directly exposing downstream service(s) for subscriptions: as you mentioned, this is not ideal. The main issue here is that each subscription might be provided by a different service (because domain boundaries) and you don't want to expose your infrastructure to clients, so you probably still want a... Gateway :).
  2. Creating a separate endpoint for Subscriptions: use a separate Apollo Server to expose Subscriptions. The Subscriptions data still comes from the micro-services — aka downstream services — but instead of directly exposing GraphQL subscriptions, they just publish the data into Redis/Postgres/GooglePubsub or any channel compatible with the PubSubEngine implementations. The Apollo server listens a resolves subscriptions.
  3. Enriching the Apollo Gateway with Subcription resolvers: until version 0.6.x, the Federated Graph composed by Apollo Gateway included the subscriptions types, but they didn't work. If you added resolvers for GQL Subscriptions at the Gateway level i.e. to the Apollo Server running the Gateway, it worked. Data for the Subscriptions was still provided by downstream services but the WebSockets and PubSub was solely handle by the ApolloServer at gateway.
  4. Not using Subscriptions at all and provide real-time updated through a different channel.

We decided to try 3, because it allowed us to give a single GQL Schema to our clients, while keeping the responsibility of defining the schema to the downstream services. Furthermore, considering that subscription resolvers were really simple — just some basic filtering based on the input parameters — we dynamically generated the resolvers after calling gateway.load().

Last week I updated to the latest version of Apollo Gateway and found the problem described on this issue. Having Subscription types in the downstream services causes a GraphQLSchemaValidationError.

GraphQLSchemaValidationError: [accounts] Subscription -> `Subscription` is an extension 
type, but `Subscription` is not defined

The error is confusing because it doesn't give a clue about Subscription not being supported by Apollo Gateway. In fact, it's actually not fully related and that's why I open the PR.

IMHO, given that Apollo Gateway doesn't support subscriptions, I would have expected the following behaviour:

If subscriptions are set to false, as described in the docs (see below), Apollo Server would just remove any Subscription type from the Federated Graph.

// https://www.apollographql.com/docs/apollo-server/federation/implementing/
const server = new ApolloServer({  
  gateway,

  // Currently, subscriptions are enabled by default with Apollo Server, however,
  // subscriptions are not compatible with the gateway.  We hope to resolve this
  // limitation in future versions of Apollo Server.  Please reach out to us on
  // https://spectrum.chat/apollo/apollo-server if this is critical to your adoption!
  subscriptions: false,
});

Otherwise, the subscription type would be composed into the Federated graph and a warning explaining that Subscriptions won't work is logged.

Reasoning: It would be easier to adopt Apollo Gateway if teams using Apollo Server don't need to change anything on the existing services (unless they want to extend other service's types), and they should only know that subscriptions won't work at the Gateway (the warning would make this clear).

As explained above, there are alternative to get subscriptions working, but that's completely optional.

Once subscriptions are supported by Apollo Gateway, the warning is removed and teams using Apollo Gateway would need to decide whether they expose them through the gateway or not (subscriptions: false).

I created a Codesandbox forking James Baxley's gateway example. You will need to fork it and change something on index.js to trigger the reload and see the GraphQLSchemaValidationError in the terminal. I had to manually load the Gateway to avoid the error thrown when gateway is defined and subscription is not false.

https://codesandbox.io/s/federation-gateway-error-with-subscriptions-6s7bt

Edit: the GraphQLSchemaValidationError is thrown, regardless of Apollo Server initialized with gateway and subscription: false properties: https://codesandbox.io/s/federation-gateway-without-loading-manually-hwzbl

jsangilve avatar Oct 06 '19 11:10 jsangilve

I get this error without subscription. I'm at the first stage of debugging this so I can't provide much more info.

It only occurs in production (I can't reproduce locally yet), with pm2 running multiple instances in cluster mode.

'[tracker] Registration -> Registration is an extension type, but Registration is not defined in any service'

aquiseb avatar Jan 28 '20 22:01 aquiseb

Workaround for this issue is to expose _service query with sdl property only with Queries and Mutations without augmenting schema from @apollo/federation

import { GraphQLObjectType, GraphQLSchema, GraphQLString, printSchema } from 'graphql';

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
      _service: {
        type: new GraphQLObjectType({
          name: 'GraphqlFederation',
          fields: () => ({
            sdl: {
              type: GraphQLString,
            },
          }),
        }),
        resolve: () => ({
          sdl: printSchema(
            new GraphQLSchema({
              query: schema.getQueryType(),
              mutation: schema.getMutationType(),
            })
          ),
        }),
      },
    }),
  }),
});

With this approach you can have 1 Subscription server providing Pub/Sub capabilities and multiple Federated graphs. The client will subscribe to this one particular server and for queries and mutation you can use ApolloGateway.

Regards!

Stradivario avatar Apr 29 '20 14:04 Stradivario

We encountered same issue with Subscriptions.

@abernix our use-case is that we use federation for queries & mutations, while subscriptions are served by separate endpoint. But we still want to serve combined schema to the playground (graphiql)

So we try to run composeAndValidate() with schemas from all services and get error above

tot-ra avatar Jun 30 '21 13:06 tot-ra

@tot-ra We understand and we continue to investigate ways to improve the options we offer for employing Subscriptions and taking advantage of Federation. Today, our only concrete suggestion is offered in this blog post.

abernix avatar Jul 16 '21 10:07 abernix

@abernix yes, we've implemented same thing Mandi is doing. It doesn't solve the problem with schema validation though, because she is not running composeAndValidate. She is just merging gateway schema with built-in subscriptions https://github.com/apollosolutions/federation-subscription-tools/blob/main/src/utils/schema.js#L27

What we want is to validate & register subscription schema in our schema-registry - for audit, naming collisions & codestyle checks. But since we rely on composeAndValidate it blocks us.

https://github.com/pipedrive/graphql-schema-registry/blob/be85c81ca8a3c2ab56be036cd6b91dde8d97dd27/app/helpers/federation.js#L24

tot-ra avatar Sep 07 '21 11:09 tot-ra

Hi. We are using Managed federation and we also need Subscriptions in our federated graphs. I've managed to implement them on our Apollo Gateway by following this tutorial https://www.apollographql.com/blog/backend/federation/using-subscriptions-with-your-federated-data-graph/ and it works.

However the problem is in our CI/CD pipeline we cannot publish/check a subgraph that contains Subscription type into Apollo Studio (registry). Previously we were dynamically removing Subscription type from schema we were about to check/publish in our CI/CD, it was lame but it worked... But since now we actually need to have Subscription in our schema we cannot do it anymore and we are stuck with following error we get from Rover:

Checking the proposed schema for subgraph ident against *****
error[E029]: Encountered 1 build error while trying to build subgraph "*" into supergraph "*****@production".

Caused by:
    Encountered 1 build error while trying to build the supergraph.
    EXTENSION_WITH_NO_BASE: [ident] Subscription -> `Subscription` is an extension type, but `Subscription` is not defined in any service
    
        The changes in the schema you proposed for subgraph ident are incompatible with supergraph *****@production. See https://www.apollographql.com/docs/federation/errors/ for more information on resolving build errors.

I've investigated the Rover code and it seems the error originates from https://graphql.api.apollographql.com/api/graphql which is beyond our control :)

Is there a way to bypass this error while still being able to have Subscription in federated schema on Apollo Studio?

michjak-szymanski avatar Nov 30 '21 15:11 michjak-szymanski

having waited for subscriptions support on federation 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

sammysaglam avatar Jan 20 '22 19:01 sammysaglam

I just second to @tot-ra we have exactly the same use case with the difference we use Rover / Apollo Studio.

What we want is to validate & register subscription schema in our schema registry.

Question to you @tot-ra, have you been able to overcome it in your solution? Looks pretty dope! 🙏

jukben avatar Apr 21 '22 07:04 jukben

I get this error without subscription. I'm at the first stage of debugging this so I can't provide much more info.

It only occurs in production (I can't reproduce locally yet), with pm2 running multiple instances in cluster mode.

'[tracker] Registration -> Registration is an extension type, but Registration is not defined in any service'

Did you resolve this issue? I am currently the same

min-proj avatar Sep 09 '22 03:09 min-proj