The GraphQL Type System & Schema
📐 The GraphQL Type System & Schema
Scalar Types
GraphQL ships with five built-in scalar types:
| Scalar | Meaning |
|---|---|
Int | A signed 32-bit integer |
Float | A signed double-precision floating-point value |
String | UTF-8 text |
Boolean | true or false |
ID | Serialized as a string, but semantically an identifier — not meant to be treated as human-readable text |
Object Types
The ! means non-null — id and name are guaranteed to always have a value; email and age may be null.
Lists: Where Nullability Gets Confusing
The ! can apply to the list itself, the items inside it, both, or neither — each combination means something different:
| Syntax | Meaning |
|---|---|
[Post] | The list itself might be null; if present, individual items might also be null |
[Post!] | The list itself might be null; but if present, every item is guaranteed non-null |
[Post]! | The list is guaranteed to exist (even if empty); but individual items inside it might be null |
[Post!]! | The list is guaranteed to exist, and every item in it is guaranteed non-null — the strictest, and most common, form |
🚪 Query and Mutation: The Entry Points
Two special types define every operation an API supports — nothing is queryable or writable unless it's listed here:
A Small Complete Schema
Query — Reading Only
Every possible top-level read operation is listed as a field on type Query — Chapter 1's user(id: 42) { name } query is calling the user field defined here.
Mutation — Writing Data
Write operations get their own root type. Chapter 4 covers mutations and input types in depth — for now, note that they're just another set of fields, defined the same way as Query's.
Enums
📝 Schema-First Design
The schema is normally written before the resolver functions that implement it — a contract-first approach, similar in spirit to the schema-first validation pattern from the TypeScript Real World Applications course. This lets frontend and backend teams work against an agreed shape in parallel, and lets tools generate documentation, type checking, and even mock servers directly from the schema itself.
Implicit Contract vs Explicit Schema
REST
The "contract" is whatever the JSON response happens to contain — discoverable only through documentation (if it exists and is current) or trial and error.
GraphQL
The schema is the contract — machine-readable, and queryable by clients themselves via introspection, which is what powers tools like GraphiQL's autocomplete.
💻 Coding Challenges
Challenge 1: Write a Schema for a Comment System
Write SDL for a Comment type (with id, text, and an author field pointing to a User) and add a comments: [Comment!]! field to the Post type from this chapter's example schema.
Goal: Practice writing object type definitions and connecting them to existing types.
Challenge 2: Explain the Four List Nullability Forms
For each of [Post], [Post!], [Post]!, and [Post!]!, write one sentence describing what a client's code would need to defensively check for that the other three forms wouldn't require.
Goal: Practice reasoning about nullability from the client's perspective, not just the schema's.
Challenge 3: Add an Enum to the Schema
Add a status: PostStatus! field to the Post type, using the PostStatus enum from this chapter, and explain why an enum is a better fit here than a plain String.
Goal: Practice choosing the right type for a field with a fixed set of valid values.
It's easy to write [Post]! when [Post!]! was actually intended, or vice versa — the ! placement is subtle and the two forms look nearly identical. The practical difference is real: with [Post]!, client code technically has to check every individual item in the list for null before using it, even though the list itself is guaranteed to exist; with [Post!]!, only an empty array is possible, never a null entry inside it. Getting this wrong either forces defensive null-checks that should never have been necessary, or — worse — lets a client assume every item is safe to use when the schema never actually guaranteed that.
🎯 What's Next
With the schema's shape defined, the next chapter covers how it actually gets filled with real data: Queries & Resolvers — the resolver function signature, and how a query maps down through nested fields.