Skip to content

Types and Fields

Object types are the workhorses of a GraphQL schema. Almost everything a client touches is an object type with fields, and the way you choose those fields — their names, their types, the arguments they accept — is most of what schema design is. This lesson goes deep on the object type and the four things you can do with its fields.

An object type is a named bundle of fields. Each field has a name and a type. Here is a type modelling a recipe in a cooking app:

type Recipe {
id: ID!
title: String!
servings: Int!
prepMinutes: Int
}

Recipe has four fields. Three of them resolve to scalars — an ID, a String, and two Int values — and the field’s type is a promise about what the client receives. A field name should read as a property of the thing: title, servings, prepMinutes. Prefer clear domain nouns over abbreviations; the schema is read far more often than it is written.

A field can take arguments, just like a function parameter. Arguments let a single field serve many shapes of request. The classic use is a lookup field on the root Query type:

type Query {
recipe(id: ID!): Recipe
recipes(first: Int, search: String): [Recipe!]!
}

recipe takes a required id and returns one recipe (or null if none matches). recipes takes optional first and search arguments to page and filter a list. Arguments are not limited to root fields — any field can take them. A common pattern is letting a field reshape its own result:

type Recipe {
id: ID!
title: String!
description(maxLength: Int): String
}

Here the client can ask for a truncated description by passing maxLength, and the resolver does the trimming. The argument lives on the field that uses it.

Descriptions — documentation in the schema

Section titled “Descriptions — documentation in the schema”

GraphQL has documentation built in. Any type, field, or argument can carry a description written as a string literal directly above it. These descriptions show up in tooling, in GraphiQL, and in generated docs:

"""
A single dish a user can cook, including timing and serving information.
"""
type Recipe {
"Stable unique identifier for the recipe."
id: ID!
"Human-readable name shown in listings and headers."
title: String!
"How many portions the recipe yields."
servings: Int!
}

A triple-quoted block handles multi-line text; a single-quoted string suffices for one line. Descriptions are not decoration — they are part of the contract that humans read. Write them as if explaining the field to a developer who has never seen your domain.

Section titled “Relationships — how types link into a graph”

The moment a field’s type is another object type, your schema stops being a list of flat records and becomes a graph. A recipe has an author; an author has recipes:

type Recipe {
id: ID!
title: String!
author: Author!
}
type Author {
id: ID!
name: String!
recipes: [Recipe!]!
}

The author field on Recipe points at the Author type, and the recipes field on Author points back. A client can start at a recipe, step into its author, and walk to that author’s other recipes — all in one query. These linking fields are the edges of your graph, and choosing them well is what makes an API feel cohesive.

flowchart LR
  Query["Query"]
  Recipe["type Recipe"]
  Author["type Author"]
  Args["Field arg: recipe(id: ID!)"]
  Query --> Args
  Args --> Recipe
  Recipe -->|"author"| Author
  Author -->|"recipes"| Recipe
Object types linked by fields form the graph clients traverse.

The example below defines Recipe and Author, gives the recipe field an argument, and runs a query that traverses the relationship between the two types. Press Run.

JavaScript

The query supplied id as an argument, received a single Recipe, then stepped into author and back out to that author’s recipe list. One round trip, several types — that is the graph at work.

What turns a flat collection of object types into a traversable graph?
Where can a field argument be declared?
In SDL, how do you attach documentation to a type or field?