Circular References in GraphQL Type Definitions

March 19, 2016

When defining GraphQL types, it’s common to run into situations when two types reference each other. This is a problem because one type will be undeclared or undefined when the other is evaluated.

// Bad, `Item` is not defined (JavaScript actually means *undeclared* here)
const User = new GraphQLObjectType({
  name: 'User',
  fields: {
    id   : { type: GraphQLString },
    email: { type: GraphQLString },
    items: {
      type: new GraphQLList(Item),
      resolve: () => { /* resolve function to get user's items */ }
    },
  }
})

const Item = new GraphQLObjectType({
  name: "Item",
  fields: {
    id:        { type: GraphQLString },
    name:      { type: GraphQLString },
    user: {
      type: User,
      resolve: () => { /* resolve function to get user of item */ }
    }
  }
})
// doesn't work either - `Item` is undefined, but `type` expects a GraphQL type
let Item // declared, but has value of undefined
const User = new GraphQLObjectType({
  name: 'User',
  fields: {
    id   : { type: GraphQLString },
    email: { type: GraphQLString },
    items: {
      type: new GraphQLList(Item),
      resolve: () => { /* resolve function to get user's items */ }
    },
  }
})

Item = ...

To fix this, the reference JavaScript implementation allows us to indicate the fields using a function that returns an object, instead of a plain object. This function is lazily evaluated during runtime, so we will not run into problems with the interpreter.

// Works!
const User = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
    id   : { type: GraphQLString },
    email: { type: GraphQLString },
    items: {
      type: new GraphQLList(Item),
      resolve: () => { /* resolve function to get user's items */ }
    },
  })
})

const Item = new GraphQLObjectType({
  name: "Item",
  fields: () => ({
    id:        { type: GraphQLString },
    name:      { type: GraphQLString },
    user: {
      type: User,
      resolve: () => { /* resolve function to get user of item */ }
    },
  })
})