Pothos
Guide

Unions

A union lets a field return one of several object types. Unlike an interface, a union has no fields of its own, and its members don't need to share any fields.

Defining Union Types

This example uses two kinds of giraffe facts. Each has a backing model and an object reference:

import SchemaBuilder from '@pothos/core';

const builder = new SchemaBuilder({});

const GiraffeStringFactRef = builder
  .objectRef<{ factKind: 'string'; fact: string }>('GiraffeStringFact')
  .implement({
    fields: (t) => ({
      fact: t.exposeString('fact'),
    }),
  });

const GiraffeNumericFactRef = builder
  .objectRef<{ factKind: 'number'; fact: string; value: number }>('GiraffeNumericFact')
  .implement({
    fields: (t) => ({
      fact: t.exposeString('fact'),
      value: t.exposeFloat('value'),
    }),
  });

const GiraffeFactRef = builder.unionType('GiraffeFact', {
  types: [GiraffeStringFactRef, GiraffeNumericFactRef],
  resolveType: (fact) => {
    switch (fact.factKind) {
      case 'string':
        return GiraffeStringFactRef;
      case 'number':
        return GiraffeNumericFactRef;
    }
  },
});

The types array lists the union's object types. Pothos infers the union's backing type from these members, so fact in resolveType can have either backing shape.

The resolveType function selects a member object type for each returned value. Here, the factKind property distinguishes the two shapes. It is part of the backing data, but isn't exposed as a GraphQL field. You can return an object reference or the member's GraphQL type name.

You can also use registered classes or object type names declared in SchemaTypes in the types array. See the Objects guide for these alternatives.

Using Union Types

Use the union reference as a field's return type. A list can contain values for any of its members. $inferType gives us the union's backing type for the data array:

const facts: typeof GiraffeFactRef.$inferType[] = [
  {
    factKind: 'string',
    fact: 'Each giraffe has a unique pattern of spots.',
  },
  {
    factKind: 'number',
    fact: 'Top speed (MPH)',
    value: 35,
  },
];

builder.queryType({
  fields: (t) => ({
    giraffeFacts: t.field({
      type: [GiraffeFactRef],
      resolve: () => facts,
    }),
  }),
});

export const schema = builder.toSchema();

Querying Union Types

Use inline fragments to select fields from each member. Even though both types define fact, it must be selected through a fragment because the union itself defines no fields. You can select __typename directly on the union to identify each result's object type:

query {
  giraffeFacts {
    __typename
    ... on GiraffeStringFact {
      fact
    }
    ... on GiraffeNumericFact {
      fact
      value
    }
  }
}
{
  "data": {
    "giraffeFacts": [
      {
        "__typename": "GiraffeStringFact",
        "fact": "Each giraffe has a unique pattern of spots."
      },
      {
        "__typename": "GiraffeNumericFact",
        "fact": "Top speed (MPH)",
        "value": 35
      }
    ]
  }
}

Other ways to resolve a union

Without a resolveType function, GraphQL can use a __typename property on the returned data or an isTypeOf check on the member object types, just as it does for interfaces. If your application uses classes, set isTypeOf: (value) => value instanceof YourClass on each member object type and return class instances. Registering a class does not add this check automatically. If an object defines isTypeOf, that check must pass even when its type was selected by resolveType or __typename.

On this page