Input Objects
Input objects group related argument values. Define one with builder.inputType, then use the
returned ref as an argument type. Pothos infers the input shape from its fields.
Creating input objects
The examples through “Recursive inputs” build on the same schema. Start with a backing model and an object ref for the mutation result:
import SchemaBuilder from '@pothos/core';
type Giraffe = {
name: string;
birthdate: string;
height: number;
};
const builder = new SchemaBuilder({});
const giraffes: Giraffe[] = [];
const GiraffeRef = builder.objectRef<Giraffe>('Giraffe').implement({
fields: (t) => ({
name: t.exposeString('name'),
birthdate: t.exposeString('birthdate'),
height: t.exposeFloat('height'),
}),
});
builder.queryType({
fields: (t) => ({
giraffes: t.field({ type: [GiraffeRef], resolve: () => giraffes }),
}),
});Input objects and output objects are separate GraphQL types, even when they have the same fields.
Here, GiraffeInput describes the argument and Giraffe describes the result. The array stores the
created giraffes in memory for this example.
const GiraffeInput = builder.inputType('GiraffeInput', {
fields: (t) => ({
name: t.string({ required: true }),
birthdate: t.string({ required: true }),
height: t.float({ required: true }),
}),
});
builder.mutationType({
fields: (t) => ({
createGiraffe: t.field({
type: GiraffeRef,
args: {
input: t.arg({ type: GiraffeInput, required: true }),
},
resolve: (_root, { input }) => {
giraffes.push(input);
return input;
},
}),
}),
});required: true on the argument requires an input object. Each input field has its own
requiredness; here all three fields must also be provided and cannot be null.
mutation {
createGiraffe(input: { name: "Gina", birthdate: "2020-03-15", height: 4.8 }) {
name
height
}
}{ "data": { "createGiraffe": { "name": "Gina", "height": 4.8 } } }Recursive inputs
Input objects can reference other input refs directly. For circular references, declare the input
shape explicitly with builder.inputRef so TypeScript does not have to infer it through the cycle.
Create the ref before implementing its fields.
Add this input and mutation field to the builder above:
interface RecursiveGiraffeInputShape {
name: string;
birthdate: string;
height: number;
friends?: RecursiveGiraffeInputShape[] | null;
}
const RecursiveGiraffeInput = builder.inputRef<RecursiveGiraffeInputShape>(
'RecursiveGiraffeInput',
);
RecursiveGiraffeInput.implement({
fields: (t) => ({
name: t.string({ required: true }),
birthdate: t.string({ required: true }),
height: t.float({ required: true }),
friends: t.field({
type: [RecursiveGiraffeInput],
required: { list: false, items: true },
}),
}),
});
function createGiraffes(input: RecursiveGiraffeInputShape): Giraffe[] {
const giraffe: Giraffe = {
name: input.name,
birthdate: input.birthdate,
height: input.height,
};
return [giraffe, ...(input.friends ?? []).flatMap(createGiraffes)];
}
builder.mutationField('createGiraffeWithFriends', (t) =>
t.field({
type: [GiraffeRef],
args: {
input: t.arg({ type: RecursiveGiraffeInput, required: true }),
},
resolve: (_root, { input }) => {
const created = createGiraffes(input);
giraffes.push(...created);
return created;
},
}),
);
const schema = builder.toSchema();The friends list can be omitted or set to null, but its items cannot be null. The TypeScript
shape includes both optionality and null to match those values. The resolver uses each friend's
own fields and follows nested friends lists, returning the parent before its descendants.
mutation {
createGiraffeWithFriends(input: {
name: "Gina", birthdate: "2020-03-15", height: 4.8
friends: [{
name: "George", birthdate: "2021-06-01", height: 4.5
friends: [{ name: "Gemma", birthdate: "2022-08-10", height: 3.9 }]
}]
}) {
name
height
}
}{
"data": {
"createGiraffeWithFriends": [
{ "name": "Gina", "height": 4.8 },
{ "name": "George", "height": 4.5 },
{ "name": "Gemma", "height": 3.9 }
]
}
}Declaring inputs in SchemaTypes
Alternatively, register input shapes in the builder's Inputs map to reference them by name.
To use this approach for createGiraffe, keep the import and Giraffe backing model from the first
example and replace its builder declaration with:
const builder = new SchemaBuilder<{
Inputs: {
GiraffeInput: Giraffe;
};
}>({});Keep the giraffes array, GiraffeRef, and query definition. Replace the GiraffeInput ref
declaration with this definition; registering a shape alone does not create the GraphQL input type.
builder.inputType('GiraffeInput', {
fields: (t) => ({
name: t.string({ required: true }),
birthdate: t.string({ required: true }),
height: t.float({ required: true }),
}),
});In the createGiraffe mutation, replace the input argument's t.arg(...) expression with:
t.arg({ type: 'GiraffeInput', required: true })Keep the rest of that mutation unchanged and call builder.toSchema() after defining it. The first
mutation and result on this page also work with this schema; the recursive example is independent
of this alternative.