Objects
We'll define a GraphQL type for a giraffe, using a TypeScript interface to describe its data.
Defining an Object type
Create schema.ts with a builder and a reference for the Giraffe type:
import SchemaBuilder from '@pothos/core';
interface Giraffe {
name: string;
birthday: Date;
heightInMeters: number;
}
const builder = new SchemaBuilder({});
const GiraffeRef = builder.objectRef<Giraffe>('Giraffe');The interface describes the backing model: the data returned by resolvers for this type.
objectRef associates that model with the GraphQL name Giraffe. We can use the ref when defining
fields that return a giraffe.
Add some fields
Next, call implement to define the object's fields. It accepts the options for the object type,
including an optional description:
GiraffeRef.implement({
description: 'A giraffe in the zoo.',
fields: (t) => ({
name: t.exposeString('name'),
height: t.exposeFloat('heightInMeters'),
birthYear: t.int({
resolve: (giraffe) => giraffe.birthday.getUTCFullYear(),
}),
}),
});The fields function receives a field builder, usually named t. Here, t.exposeString('name')
reads the name property from the backing model. The height field reads heightInMeters, giving
that property a different name in the GraphQL schema.
For birthYear, we write a resolver that computes an integer from the giraffe's birthday. Pothos
infers the resolver's first argument as Giraffe from the ref.
The backing model and GraphQL fields can have different shapes. Clients can query name, height,
and birthYear; birthday is available to resolvers but isn't exposed as a field. We can add
computed fields without adding properties to the backing model. The fields guide
covers more field options.
Add a query
Add a field to the root Query type that returns a giraffe, then build the schema:
builder.queryType({
fields: (t) => ({
giraffe: t.field({
type: GiraffeRef,
resolve: () => ({
name: 'James',
birthday: new Date(Date.UTC(2012, 11, 12)),
heightInMeters: 5.2,
}),
}),
}),
});
export const schema = builder.toSchema();The type: GiraffeRef option tells Pothos to check that the resolver returns data matching the
Giraffe interface. That returned object becomes the first argument to the resolvers for name,
height, and birthYear.
Create a server
builder.toSchema() builds a standard GraphQLSchema. To serve it with graphql-yoga, install
graphql-yoga and save this as server.ts alongside schema.ts:
import { createServer } from 'node:http';
import { createYoga } from 'graphql-yoga';
import { schema } from './schema';
const yoga = createYoga({ schema });
const server = createServer(yoga);
server.listen(3000);Query your data
Run npx tsx server.ts, then open http://localhost:3000/graphql
and run this query:
query {
giraffe {
name
birthYear
height
}
}The result is:
{
"data": {
"giraffe": {
"name": "James",
"birthYear": 2012,
"height": 5.2
}
}
}Different ways to define Object types
This guide uses object refs with a TypeScript interface. You can also use an existing class or register backing models by name on the builder. These forms can be used together in the same schema.
Using Refs
You can create and implement a ref in one expression:
const GiraffeRef = builder.objectRef<Giraffe>('Giraffe').implement({
fields: (t) => ({
name: t.exposeString('name'),
}),
});For types that reference each other, TypeScript may need ref creation and implement to be separate
statements, as in the walkthrough above. See circular references for details.
You can also pass a ref to builder.objectType(GiraffeRef, { ... }) with the same options.
Using classes
If your app already represents its data with classes, prefer using those classes as backing models.
Pass the class to builder.objectType: its instance type becomes the backing model, and the class
itself can be used as a field's type:
class Giraffe {
constructor(
public name: string,
public birthday: Date,
public heightInMeters: number,
) {}
}
builder.objectType(Giraffe, {
name: 'Giraffe',
fields: (t) => ({
name: t.exposeString('name'),
}),
});
builder.queryType({
fields: (t) => ({
giraffe: t.field({
type: Giraffe,
resolve: () => new Giraffe('James', new Date(Date.UTC(2012, 11, 12)), 5.2),
}),
}),
});The name option supplies the GraphQL type name. When resolving an
interface or union, you can also use an explicit
isTypeOf: (value) => value instanceof Giraffe option to identify instances of the class.
Using SchemaTypes
You can declare backing models in SchemaTypes and pass it to SchemaBuilder. This lets you
reference object types by their GraphQL names instead of importing refs. Using the Giraffe
interface from above:
interface SchemaTypes {
Objects: {
Giraffe: Giraffe;
};
}
const builder = new SchemaBuilder<SchemaTypes>({});
builder.objectType('Giraffe', {
fields: (t) => ({
name: t.exposeString('name'),
}),
});
builder.queryType({
fields: (t) => ({
giraffe: t.field({
type: 'Giraffe',
resolve: () => ({
name: 'James',
birthday: new Date(Date.UTC(2012, 11, 12)),
heightInMeters: 5.2,
}),
}),
}),
});The Objects mapping tells Pothos which backing model belongs to each name. Here, it types the
Giraffe field resolvers and checks the object returned by the giraffe query. You still define
the GraphQL fields with builder.objectType; declaring a backing model doesn't expose its properties.
The SchemaBuilder guide covers the other type settings.