apollographql / federation

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

[Federation] Identical Interfaces not allowed #344

Open davidbarratt opened 5 years ago

davidbarratt commented 5 years ago

I'm using the latest verion of apollo server and apollo gateway. I've implemented two federated endpoints (in PHP) here:

Both of these endpoints use the same code base, but different databases. I've ensured that all of the interfaces are identical, and the objects are namespaced. For instance MediaWikiRevision is an interface of a revision that is identical. There is a TatooineRevision and a JakkuRevision that both implement the identical interface. I've also ensured that the fields on the interface only return other interfaces (not objects).

However, when I try to use these in Apollo Gateway I get the following error:

GraphQLSchemaValidationError: Field "MediaWikiRevision.revid" can only be defined once. Field "MediaWikiRevision.parent" can only be defined once. Field "MediaWikiRevision.user" can only be defined once. Field "MediaWikiRevision.anon" can only be defined once. Field "MediaWikiRevision.timestamp" can only be defined once. Field "MediaWikiRevision.size" can only be defined once. Field "MediaWikiRevision.sha1" can only be defined once. Field "MediaWikiRevision.comment" can only be defined once. Field "MediaWikiRevision.parsedcomment" can only be defined once. Field "MediaWikiRevision.tags" can only be defined once. Field "MediaWikiRevision.roles" can only be defined once. Field "MediaWikiRevision.slot" can only be defined once. Field "MediaWikiRevision.slots" can only be defined once. There can be only one type named "MediaWikiRevision".

According to the docs it's totally accaptable to have overlapping types, as long as they are identical. Doing a google search for the error reveals this unanswered stack overflow question.

I'm not sure what the problem is, but according to the documentation, this error shouldn't be thrown... am I missing something?

davidbarratt commented 5 years ago

any ideas?

Jackson3195 commented 5 years ago

Hey, I'm getting the same issue but I'm not using Apollo Gateway.

I used the demo project here but tweaked the architecture style slightly to work with Azure functions (Serverless).

I took the demo files and slightly tweaked them, an example below:

import { DocumentNode } from 'graphql';
import { gql } from 'apollo-server-azure-functions';
import { GQLResolver } from '@interfaces/';

// Schema
export const accountTypeDefs: DocumentNode = gql`

  extend type Query {
    me: User
  }

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

`;

// Resolvers
export const accountResolvers: GQLResolver = {
  Query: {
    me () {
      return users[0];
    },
  },
  User: {
    __resolveReference (object: User) {
      return users.find((user) => user.id === object.id);
    },
  },
};

// Dummy data
const users: User[] = [
  {
    id: '1',
    name: 'Ada Lovelace',
    birthDate: '1815-12-10',
    username: '@ada',
  },
  {
    id: '2',
    name: 'Alan Turing',
    birthDate: '1912-06-23',
    username: '@complete',
  },
];

interface User {
  id: string;
  name: string;
  birthDate: string;
  username: string;
}

Then combine them into a single schema such as this:

// Required Imports
import { GraphQLSchema } from 'graphql';
import { buildFederatedSchema } from '@apollo/federation';

// Entity Imports
import { accountTypeDefs, accountResolvers } from './accounts';
import { inventoryTypeDefs, inventoryResolvers } from './inventory';
import { productTypeDefs, productResolvers } from './products';
import { reviewsTypeDefs, reviewResolvers } from './reviews';

// Generate the schema via import entities
export const schema: GraphQLSchema = buildFederatedSchema([
  {
    typeDefs: accountTypeDefs,
    resolvers: accountResolvers,
  },
  {
    typeDefs: productTypeDefs,
    resolvers: productResolvers,
  },
  {
    typeDefs: inventoryTypeDefs,
    resolvers: inventoryResolvers,
  },
  {
    typeDefs: reviewsTypeDefs,
    resolvers: reviewResolvers,
  },
]);

which is then initialised by the server

const server: ApolloServer = new ApolloServer({
  schema,
  playground: {
    endpoint: '/api/v1',
      settings: {
        'editor.theme': 'dark',
      },
    },
  });

But when running locally, run into the following:

image

I've noticed that any service implementing an "@external" tag on an attribute; based on the example, throws the error. The errors are therefore are caused by the "Inventory" and "Review" services extending types Product/User.

I am unsure what I am missing?

davidbarratt commented 4 years ago

I figured out where the problem is... but not why it's a problem.

Given this schema:

interface MediaWikiUser {
    userid: Int
    name: String
}

interface MediaWikiRevisionSlot {
    size: Int
    sha1: String
    contentmodel: String
    contentformat: String
    content: String
}

interface MediaWikiRevision {
    revid: Int
    parent: MediaWikiRevision
    user: MediaWikiUser
    anon: Boolean
    timestamp: String
    size: Int
    sha1: String
    comment: String
    parsedcomment: String
    tags: [String!]
    roles: [String!]
    slot(role: String!): MediaWikiRevisionSlot
    slots(role: [String]): [MediaWikiRevisionSlot]!
}

The problem is in MediaWikiRevision, removing either slot(role: String!): MediaWikiRevisionSlot or slots(role: [String]): [MediaWikiRevisionSlot]! fixes the problem. I really don't understand why though.

davidbarratt commented 4 years ago

Cross posted: T247673

davidbarratt commented 4 years ago

Here is the stack trace if that is helpful:

There can be only one type named "MediaWikiRevision".
    at ApolloGateway.createSchema (node_modules/@apollo/gateway/dist/index.js:222:19)
    at ApolloGateway.<anonymous> (node_modules/@apollo/gateway/dist/index.js:193:32)
    at Generator.next (<anonymous>)
    at fulfilled (node_modules/@apollo/gateway/dist/index.js:5:58)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)