3
votes

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
What is you goal? You can have just one User type. Do you want to merge them or do you want just the second User type? What happens in your case is the default behavior taking the first encountered type of all the types with the same name.Reto Aebersold
Ideally, I would like to extend the User Type. I've edited my Question accordingly.Nicolai Schmid
The Problem is, that these aren't local Schemas, so I cannot easily change type User to extend type User. I fetch them using makeRemoteExecutableSchemaNicolai Schmid

2 Answers

3
votes

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.

3
votes

This should help

extend type User {
   age: String!
}