Apollo Graphql Schema Stitching Conflict

Viewed 1327

I have a problem regarding GraphQL Schema stitching. I have two Graphql Schemas:

type Name {
   firstname: String!
   lastname: String!
}

type Address {
   street: String!
   number: Int!
}

type User {
   name: Name!
   address: Address!
}

type Query {
   user(userId: String!): User
}

and

type User {
   age: String!
}

type Query {
   user(userId: String!): User
}

I now tried to merge the schemas using graphql-tools's mergeSchemas Function:

const schema = mergeSchemas({
   schemas: [schema1, schema2]
});

But instead of what I'm trying to achieve (an extended User Type):

type Name {
   firstname: String!
   lastname: String!
}

type Address {
   street: String!
   number: Int!
}

type User {
   name: Name!
   address: Address!
   age: String!
}

type Query {
   user(userId: String!): User
}

it resulted in this: type Name { firstname: String! lastname: String! }

type Address {
   street: String!
   number: Int!
}

type User {
   name: Name!
   address: Address!
}

type Query {
   user(userId: String!): User
}

Only one of the UserTypes is displayed in the final schema. I tried using the onTypeConflict API in mergeSchemas to extend the Type but I haven't made any results.

Is there a way to merge Schemas by extending Types on Conflict?

2 Answers

Here is a possible solution to merge the object types. Maybe it makes sense to filter by type name in onTypeConflict instead of merging every type.

import cloneDeep from 'lodash.clonedeep'
import { GraphQLObjectType } from 'graphql/type/definition'
import { mergeSchemas } from 'graphql-tools'

function mergeObjectTypes (leftType, rightType) {
  if (!rightType) {
    return leftType
  }
  if (leftType.constructor.name !== rightType.constructor.name) {
    throw new TypeError(`Cannot merge with different base type. this: ${leftType.constructor.name}, other: ${rightType.constructor.name}.`)
  }
  const mergedType = cloneDeep(leftType)
  mergedType.getFields() // Populate _fields
  for (const [key, value] of Object.entries(rightType.getFields())) {
    mergedType._fields[key] = value
  }
  if (leftType instanceof GraphQLObjectType) {
    mergedType._interfaces = Array.from(new Set(leftType.getInterfaces().concat(rightType.getInterfaces())))
  }
  return mergedType
}

const schema = mergeSchemas({
  schemas: [schema1, schema2],
  onTypeConflict: (leftType, rightType) => {
    if (leftType instanceof GraphQLObjectType) {
      return mergeObjectTypes(leftType, rightType)
    }
    return leftType
  }
})

Credits: The mergeObjectTypes function was written by Jared Wolinsky.

This should help

extend type User {
   age: String!
}
Related