# Object types
URL: /docs/fundamentals/objects
Object types, the backing model behind each one, and the different ways to define them.
## Defining an object type [#defining-an-object-type]
Object refs
Classes
Builder types
```typescript playground example="fundamentals-objects"
interface ICharacter {
id: string;
name: string;
birthYear?: string;
biography?: string;
editorId: string;
}
const Character = builder.objectRef('Character');
Character.implement({
fields: (t) => ({
id: t.exposeID('id'),
name: t.exposeString('name'),
}),
});
```
```typescript playground example="fundamentals-objects-variant-classes"
class Character {
constructor(
public id: string,
public name: string,
public editorId: string,
public birthYear?: string,
public biography?: string,
) {}
}
builder.objectType(Character, {
name: 'Character',
fields: (t) => ({
id: t.exposeID('id'),
name: t.exposeString('name'),
}),
});
```
```typescript playground example="fundamentals-objects-variant-builder-types"
interface ICharacter {
id: string;
name: string;
birthYear?: string;
biography?: string;
editorId: string;
}
const builder = new SchemaBuilder<{
Objects: { Character: ICharacter };
}>({});
builder.objectType('Character', {
fields: (t) => ({
id: t.exposeID('id'),
name: t.exposeString('name'),
}),
});
```
Each tab defines the same `Character` object type. To define one we give it a name, a backing model (the TypeScript shape of the data behind the type), and the fields that make it up. In the `objectRef` form shown first, `builder.objectRef('Character')` creates a reference that carries the name and the backing model, and `.implement` supplies the fields. Each field maps data from the backing model to the GraphQL shape a client sees: `t.exposeID('id')` and `t.exposeString('name')` read those two properties straight off the backing model, and the [Fields](./fields) guide covers the field builder in detail. The **Classes** and **Builder types** tabs build the same type from a class and from a registered name, [covered below](#definition-styles).
## Type references [#type-references]
`builder.objectRef` returns a type reference: a value that stands in for the `Character` type wherever the schema needs to name it. A reference is not particular to `objectRef`. `builder.objectType` returns one too, a class used to define a type acts as its own reference, and a type name registered on the builder (the **Builder types** tab) references the type by string. Any of these can be used as a field's `type`, which is how a field on one type returns another type.
`builder.objectType` takes a reference in each of these forms as its first argument: a class, an existing object ref, or a string name registered on the builder's `Objects` generic. `implement` is the object-ref shorthand: calling `.implement(options)` on a ref is the same as passing that ref and the options to `builder.objectType`.
## The backing model [#the-backing-model]
The generic on `objectRef` (or the class's instance type, or the shape registered under a name) is the type's backing model: the value your resolvers return for the type, and the `parent` Pothos hands to every field defined on it. Whatever form the reference takes, it carries this shape, and Pothos checks the type's fields against it. `t.exposeString('name')` compiles only when `name` is a string on `ICharacter`, and any field that returns `Character` has to resolve to a value matching it.
The backing model and the GraphQL type are separate things. The backing model is whatever your resolvers work with (a database row, a plain object, a class instance, or even just a string id), while the GraphQL type is the set of fields a client can select. Exposing a property with `t.expose*` is the direct case, where a field reads a value straight off the backing model. Every other field is defined by writing a resolver, and as long as you can compute a field's value from the backing model (and the context), you can add it to the type without changing the backing model. A type backed by nothing but an id can still present a full set of fields, each resolver loading what it needs. The [Fields](./fields) guide covers computed fields and the field builder.
## Object type options [#object-type-options]
`builder.objectRef('Character')` takes two things: the type's name and, as its generic, the backing model. Everything else about the type goes to `implement`: the `fields` callback, a `description`, the [interfaces](./interfaces) the type implements, and an `isTypeOf` function for [resolving abstract types](./interfaces). `fields` is the option you pass most often, but it works like any other option on `implement`.
When a type doesn't need to be referenced before it's implemented, the two calls chain into a single expression:
```typescript playground example="fundamentals-objects"
const Race = builder.objectRef('Race').implement({
fields: (t) => ({
id: t.exposeID('id'),
name: t.exposeString('name'),
lifespan: t.exposeString('lifespan'),
}),
});
```
`builder.objectRef('Race').implement({ ... })` creates the reference and implements it in one statement. Keeping the two calls apart, as `Character` does above, is what lets two types reference each other: the reference exists before either type's fields are defined, so each can name the other. The [Circular references](../patterns/circular-references) pattern covers that case.
## Returning an object type [#returning-an-object-type]
A type reference can be the `type` of any field, on any type in the schema. Here the root `Query` type returns a list of `Character`:
```typescript playground example="fundamentals-objects"
builder.queryType({
fields: (t) => ({
characters: t.field({
type: [Character],
resolve: () => characters,
}),
}),
});
```
Wrapping the reference in an array (`type: [Character]`) makes the field a list, while a bare `type: Character` returns a single one. Because the field returns `Character`, its resolver has to return values matching the backing model, and TypeScript reports an error if it doesn't. The [Queries](./queries) guide covers the `Query` root itself.
## Definition styles [#definition-styles]
The three tabs at the top of the page define the same `Character` type in the three reference forms. All are fully supported, and a single schema can mix them freely.
**Object refs.** `builder.objectRef('Character')` states the backing model as a generic and hands back a reference to pass around.
**Classes.** `builder.objectType(Character, { ... })` defines the type from a class and uses the class's instance type as the backing model, so `parent` in every resolver is an instance of the class, methods and getters included. This fits well when your app already keeps classes for its data. Because a class is also a value at runtime, you can write `isTypeOf: (value) => value instanceof Character`, an `instanceof` check that identifies the type when an [interface or union](./interfaces) value is resolved (`isTypeOf` is always an option you set; it isn't derived from the class).
**Builder types.** Registering a type on the builder's `Objects` generic maps a name to its backing model, so you can refer to the type by that string name (`t.field({ type: 'Character' })`) anywhere in the schema. This keeps your type definitions in one place instead of importing a reference into every file that uses them. Registering the name only tells TypeScript about the type; you still create it at runtime with `builder.objectType('Character', { ... })`.