Skip to content

Circular References in GraphQL Type Definitions

Posted on: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 */
      },
    },
  }),
});