Graphql Union Resolver

June 03, 2021

The Graphql documentation glosses over some basic points in teaching unions. These caused me some issues and maybe you too. It's recommended that you read the documentation first.

In this example I have a User type and I want to add an avatar field. This can either be an AvatarImage or AvatarIntials.

Code

schema.graphql

type User {
  ...
  avatar = UserAvatar!
}

union UserAvatar = AvatarImage | AvatarInitials

type AvatarImage {
  image: Image!
}

type AvatarInitials {
  initials: String!
}

Create a file to create these classes in javascript or typescript.

UserAvatar.ts

export abstract class UserAvatar {
    abstract get typename(): string;
  }

  export class AvatarImage extends UserAvatar {
    readonly image: ImageModel;

    constructor(image: ImageModel) {
      super();
      this.image = image;
    }

    get typename() {
      return "AvatarImage";
    }
  }

  export class AvatarInitials extends UserAvatar {
    readonly initials: String;

    constructor(initials: String) {
      super();
      this.initials = initials;
    }

    get typename() {
      return "AvatarInitials";
    }
  }

userResolver.ts

export default {
  User: {
    id: ...,
    ...
  },
  UserAvatar: {
    __resolveType(obj: any) {
      if (obj instanceof UserAvatar) {
        return obj.typename;
      } else {
        throw new Error(`Invalid type provided for a UserAvatar instance`);
      }
    }
  },

User.ts

export default class User {
  ...
  avatar() {
    if (true /* conditional */) {
      return new AvatarImage(image)
    } else {
      return new AvatarInitials("MS")
    }
  }
}

Learnings

I missed a few things while building this. I nested avatar or UserAvatar in the User block. I also didn't know where to provide the value which turned out to be the User entity.

Errors

This helped solve my error messages of

"Abstract type \"UserAvatar\" must resolve to an Object type at runtime for field \"User.avatar\". Either the \"PUserAvatar\" type should provide a \"resolveType\" function or each possible type should provide an \"isTypeOf\" function."

"Cannot return null for non-nullable field User.avatar."