apollographql / federation

🌐  Build and scale a single data graph across multiple services with Apollo's federation gateway.
https://apollographql.com/docs/federation/
Other
664 stars 249 forks source link

Unable to run federated queries when returning a query in a mutation payload #806

Closed mvanalphen closed 2 years ago

mvanalphen commented 3 years ago

As described here, it is possible to return the Query type in a mutation payload. This allows a user to execute a query after the completion of a mutation, which is then returned with the original mutation response.

Mutation query example

For instance, take the following Mutation defined in a hypothetical federated order-graphql application (of course running behind a Apollo GraphQL Gateway):

extend type Mutation {
  cancelOrder(input: CancelOrderInput!): CancelOrderResponse
}

input CancelOrderInput {
  orderId: Float!
}

type CancelOrderResponse {
  orderId: Float!
  query: Query
}

The mutation resolver looks like this:

const orderMutations: MutationResolvers = {
  cancelOrder: (root, args, { dataSources }) => {
    const result = dataSources!.orderApi.cancelOrder(args.input);

    return {
      ...result,
      query: {}
    };
  }
};

Now, let's run the following mutation:

mutation Example ($orderId: Float!) {
  cancelOrder(input: { orderId: $orderId }) {
    query {
      order(orderId: $orderId) {
        progress {
          status
        }
      }
    }
  }
}

This works perfectly. The mutation is executed, and the order query (also defined in the order-graphql) is executed and returns the new order status or any other data the order query is able to return.

What doesn't work

What does not work, however, if we try to run a query in the mutation that is not defined in the order-graphql, but in a completely different federated GraphQL instance. For instance, the store query which is defined in a federated store-graphql application:

mutation Example ($orderId: Float!) {
  cancelOrder(input: { orderId: $orderId }) {
    query {
      order(orderId: $orderId) {
        progress {
          status
        }
      }
      store(storeId: 123) { # This is defined in the store-graphql, not in the order-graphql
        city
      }
    }
  }
}

This returns the following error: Cannot query field "store" on type "Query". The execution fails with a 400 Bad Request.

However, if we run an introspection:

mutation Mutation ($orderId: Float!) {
  cancelOrder(input: { orderId: $orderId, orderChannel: WEB }) {
    query {
      order(orderId: $orderId) {
        customerId
        progress {
          status
        }
      }
      __schema {
        queryType {
          fields {
            name
          }
        }
      }
    }
  }
}

The store query is returned:

"__schema": {
  "queryType": {
    "fields": [
      {
        "name": "order"
      },
      {
        "name": "store"
      }
    ]
    }
  }
}

Expected behaviour

Because both the order-graphl and store-graphl applications are running in the federation behind a gateway, I'd expect to be able to run any query from a mutation when returning a Query object.

I'd love to hear if there's any ways around this and if my assumptions are correct that this should work.

pcmanus commented 2 years ago

Agree that this is unexpected.

We just released the first alpha of the upcoming federation 2.0 that, amongst other things, fix this particular issue (through 9cd5c07b2b9baa0371e0ffaa2fc83b5628e9e8cb and db5b3b4abdbe81e410b0812befe4dbf9ef7f0001). Tests demonstrating this is now working can be found here.

I'm afraid fixing this on prior versions is a bit involved and this will likely remain a "known" limitation on those versions.