This chapter follows Schemas and SDL.
GraphQL types form a graph: object types are nodes with named fields, and scalars are the terminal values execution must eventually produce. Understanding nullability and polymorphism (interface, union) prevents subtle client bugs and bad cache behavior.
Object types and fields
An object type groups fields. Each field has a return type and optional arguments:
type Author { id: ID! name: String! books(first: Int = 10): [Book!]!}Every object type implicitly includes __typename, a string naming the runtime type. Clients query it when a field returns an interface or union:
{ author(id: "1") { __typename name }}Resolvers for object fields receive the parent value returned by the parent field’s resolver (often a database row or DTO).
Built-in scalars
Scalars represent leaf values. The spec defines:
| Scalar | Meaning |
|---|---|
String |
UTF-8 text |
Int |
32-bit signed integer |
Float |
signed double |
Boolean |
true or false |
ID |
opaque identifier, serialized as string |
Execution completes when every selected field resolves to a scalar (or null where allowed).
Custom scalars
Domains often need dates, decimals, or URLs. Declare a custom scalar in SDL:
scalar DateTimeThe server must implement:
- serialize — value from resolver → JSON for the response
- parseValue — variable input → internal value
- parseLiteral — AST literal in the query → internal value
Link machine-readable semantics with @specifiedBy(url: "...") when a JSON Schema or RFC defines the format.
In NestJS, register a custom scalar class with @Scalar() and wire it in the module; Apollo Server and Yoga expose similar hooks.
Enumerations
Enums restrict a field or argument to a fixed set of values:
enum OrderStatus { PENDING SHIPPED CANCELLED}Enum values are conventionally ALL_CAPS. Adding a new enum value is usually backward compatible for queries but can break exhaustive switch statements in clients—a theme in the schema evolution chapters.
Lists and non-null
Type modifiers stack:
type Example { requiredList: [String!]! # list cannot be null; elements cannot be null optionalList: [String] # list or elements may be null nullableField: String # field may be null}Misplaced ! is a common source of production errors: a resolver that throws on a non-null field nulls out ancestors per the spec’s bubbling rules.
Interfaces
An interface declares fields implementing types must provide:
interface Node { id: ID!}
type User implements Node { id: ID! email: String!}Queries can return the interface; clients use inline fragments (... on User) to reach type-specific fields.
Unions
A union expresses “one of these object types” without shared fields:
union SearchResult = Book | Author
type Query { search(text: String!): [SearchResult!]!}Clients must query __typename and spread fragments for each member type they care about.
Next: Input types and arguments.
Check your understanding
Question 1 of 7
0 of 7 checked