ardatan/graphql-mesh

Combine federated and not federated services into one

Closed this issue · 13 comments

Suppose we have something like:

articles.js localhost:4002 federated

const { ApolloServer, gql } = require("apollo-server");
const { buildFederatedSchema } = require("@apollo/federation");

const articles = [
  {
    id: "1",
    title: "article1",
    body: "article1 body"
  },
  {
    id: "2",
    title: "article2",
    body: "article2 body"
  },
  {
    id: "3",
    title: "article3",
    body: "article3 body"
  }
];

const typeDefs = gql`
  type Query {
    articles: [Article]!
    article(id: ID!): Article
  }

  type Article @key(fields: "id") {
    id: ID!
    title: String
    body: String
  }
`;

const resolvers = {
  Query: {
    articles: () => articles,
    article: (_, { id }) => articles.find(({ id: articleId }) => id === articleId)
  }
};

const server = new ApolloServer({
  schema: buildFederatedSchema([
    {
      typeDefs,
      resolvers
    }
  ])
});

server.listen({ port: 4002 }).then(({ url }) => console.log("articles:", url));

accounts.js http://localhost:4001 not federated

const { ApolloServer, gql } = require("apollo-server");

const accounts = [
  {
    id: "1",
    name: "Alex",
    username: "@mac"
  },
  {
    id: "2",
    name: "Yar",
    username: "@yar"
  }
];

const typeDefs = gql`
  type Query {
    accounts: [Account]!
    account(id: ID!): Account
  }

  type Account {
    id: ID!
    name: String
    username: String
  }
`;

const resolvers = {
  Query: {
    accounts: () => accounts,
    account: (_, { id }) => accounts.find(({ id: accountId }) => id === accountId)
  }
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen({ port: 4001 }).then(({ url }) => console.log("accounts:", url));

.meshrc.yaml

sources:
  - name: Accounts
    handler:
      graphql:
        endpoint: http://localhost:4001/graphql
    transforms:
      - federation:
          types:
            - name: Query
              config:
                extend: true
            - name: Account
              config:
                keyFields:
                  - id
  - name: Articles
    handler:
      graphql:
        endpoint: http://localhost:4002/graphql

Expected results: Schema should have _Entity union with both Account and Article

Actual results: union not being merged

The same is true for other federation features, like _Services etc

First, you are using federation transform for a schema that is already federated. If you want to consume a federated schema in GraphQL Mesh, use federation handler not graphql handler; https://graphql-mesh.com/docs/handlers/federation

Federation transform is to convert non-federated schemas to federated ones.

d-led commented

perhaps, relevant here too: federating the services via graphql-transform-federation may require adding a resolveReference resolver (?). Is there a way to do so from the mesh config?

You can use additionalResolvers to add reference resolver as in Apollo Federation specs. Sorry for the lack of documentation related to Federation in Mesh. But we're working on a detailed recipe :)

@ardatan thank you for your point I'm not really sure if I get it right

first miss understanding - you are saying that I'm using federation transformation for already federated service which is not true (take a closer look, I'm using federation transformation on Accounts service which is not federated)

second is about usage of federation handler not graphql handler, even if i will change yaml to this one:

sources:
  - name: Gateway
    handler:
      federation:
        serviceList:
          - name: Articles
            url:  http://localhost:4002/graphql
  - name: Accounts
    handler:
      graphql:
        endpoint: http://localhost:4001/graphql
    transforms:
      - federation:
          types:
            - name: Query
              config:
                extend: true
            - name: Account
              config:
                keyFields:
                  - id

There still will be only Account in entities union unfortunately

@mac2000 By the way you are right I misunderstood. So right now, I am not sure if Mesh currently supports this kind of use case. I will test and give an update soon.

Did setup micro repository - https://github.com/mac2000/graphql-mesh-issue-296

e.g.:

git clone https://github.com/mac2000/graphql-mesh-issue-296
cd graphql-mesh-issue-296
npm i
node accounts.js
node articles.js
npm start
open http://localhost:4000

hope it will be much easier to understand what i'm talking about

Just to clarify, when we are hiding our federated service by gateway it does remove all federation related directives, types, ets and then when mesh combines everything together there will be nothing left

And one more, I'm not sure but why federation specific stuff is still in output at all after mesh, where should _entities resolvers go?

PS: @d-led yep, you going to go thru 7 rounds of hell, my personal recommendation is to start with underlying library and wire up everything with it just to figure out how it all works together and only then come back to mesh, otherwise it will be way to paint full

without mesh it will be something like:

withoutmesh

wondering if we will be able to find a way to have something like:

idealworld

sources:
  - name: Accounts
    handler:
      graphql:
        endpoint: http://localhost:4001/graphql
    transforms:
      - federation:
          types:
            - name: Query
              config:
                extend: true
            - name: Account
              config:
                keyFields:
                  - id
  - name: Articles
    handler:
      graphql:
        endpoint: http://localhost:4002/graphql

So federation handler removes that union and that means you need to use graphql handler not federation one, could you try the configuration above with the canary version in this PR?
This PR fixes the union merging issue #297

I saw your last comment. Currently it is not possible with a single Mesh configuration but you can create this architecture with multiple mesh configurations(that will be almost same with the first graph).
So I'll keep this issue open (as a feature request) because I think we can implement a solution for your use case.

d-led commented

You can use additionalResolvers to add reference resolver as in Apollo Federation specs. Sorry for the lack of documentation related to Federation in Mesh. But we're working on a detailed recipe :)

@ardatan: for now, I just extended the type directly, it's at least readable. It would be interesting to see how additionalResolvers could look like.

Currently we're improving Federation on Mesh;
PR; #367
Example; https://github.com/Urigo/graphql-mesh/tree/federation-merger/examples/federation-example
Alpha version; https://github.com/Urigo/graphql-mesh/pull/367

Could you try and let us know if it works?

Available in v0.1.17