# 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]

<CodeBlockTabs defaultValue="Object refs">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="Object refs">
      Object refs
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Classes">
      Classes
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Builder types">
      Builder types
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="Object refs">
    ```typescript playground example="fundamentals-objects" 
    interface ICharacter {
      id: string;
      name: string;
      birthYear?: string;
      biography?: string;
      editorId: string;
    }

    const Character = builder.objectRef<ICharacter>('Character');

    Character.implement({
      fields: (t) => ({
        id: t.exposeID('id'),
        name: t.exposeString('name'),
      }),
    });
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Classes">
    ```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'),
      }),
    });
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Builder types">
    ```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'),
      }),
    });
    ```
  </CodeBlockTab>
</CodeBlockTabs>

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<ICharacter>('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<ICharacter>` (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<ICharacter>('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<IRace>('Race').implement({
  fields: (t) => ({
    id: t.exposeID('id'),
    name: t.exposeString('name'),
    lifespan: t.exposeString('lifespan'),
  }),
});
```

`builder.objectRef<IRace>('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<ICharacter>('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', { ... })`.
