Using Graphql Unions with Interfaces

Let’s say that we have the following interface (sorry if the example doesn’t make much sense):

type Query {
  person(name: String!): Person
}

interface Person {
  name: String!
}

type Customer {
  name: String!
  business: String
}

type Employee {
  id: ID!
  name: String!
}

With that schema, we can do the following query:

query {
  person(name: "John") {
    __typename
    ... on Person {
      name
    }
    ... on Customer {
      business
    }
    ... on Employee {
      id
    }
  }
}

That query works great! you can abstract your result from the real type if you want, and if you don’t, you can use fragments and the __typename to handle the specific cases.

If you have used union types, you might haven been in the situation where you want to use interfaces in there. But, per the graphql specification, that is not possible:

The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union. https://facebook.github.io/graphql/June2018/#sec-Unions

But, although you can’t use them in the union type, you can use the types in the union and then query by interface.

Let’s see it in action, I create a new interface and new a union type:

type Query {
  person(name: String!): PersonPayload
}

# This is INVALID, no interfaces allowed
union PersonPayload = Error | Person

# This is how it needs to be done
union PersonPayload =
  NotFoundError
  | InvalidError
  | Customer
  | Employee

# Error interface with 2 implementations
interface Error {
  message: String!
  code: String!
}

type NotFoundError implements Error {
  elementId: ID!
  message: String!
  code: String!
}

type InvalidError implements Error {
  message: String!
  code: String!
  invalidPart: String!
}

# Person interface with 2 implementations
interface Person {
  name: String!
}

type Customer {
  name: String!
  orders: [Order!]!
}

type Employee {
  id: ID!
  name: String!
  orders: [Order!]!
}

Now, although you don’t have the interfaces in the union type, you can query using those interfaces:

query {
  person(name: "John") {
    __typename
    ... on Person {
      name
    }
    ... on Error {
      message
    }
    ... on InvalidError {
      # message is "inherited" from "... on Error"
      invalidPart
    }
    # If you want something in particular for Customer, you do the same
  }
}

As you can see, you can’t use the interfaces directly in the union type, but you can still query your data using those interfaces.

Although this is not something new, it can be something that is not mentioned a lot in the usual grahpql tutorials or articles. Knowing this can open your mind to more “polymorphic” queries and mutations, giving more flexibility to you and your client while consuming your graphql API.

Hope it made you discover a new side of graphql!

Cheers

Blog Logo

Tomas Alabes

Software Engineer, author, blogger and obsessive learner, from Argentina living in Silicon Valley


Published

Image

Tomas Alabes' Blog

My personal site's blog

Back to Overview