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.