Skip to main content

Schemas and Types

Learn about the fundamental building blocks that define every GraphQL API.


What is a Schema?

A GraphQL schema is the formal contract between clients and the API. Every incoming request is validated and executed against this schema.

┌─────────────────────────────────────────────────────────────────────┐
│ SCHEMA DEFINES │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ • What types exist (Movie, Review, User) │
│ • What fields those types expose │
│ • What operations are allowed: │
│ - Query (read) │
│ - Mutation (write) │
│ - Subscription (real-time) │
│ • What inputs are accepted │
│ • What nullability guarantees the API provides │
│ │
└─────────────────────────────────────────────────────────────────────┘

Schemas are written in SDL (Schema Definition Language) and implemented by resolvers on the server.


A Simple Schema

type Movie {
id: ID!
title: String!
duration: Float!
}

type Query {
movie(id: ID!): Movie
movies: [Movie!]!
}

Reading this schema:

ElementMeaning
MovieAn object type with 3 fields
id: ID!Non-null unique identifier
title: String!Non-null string
duration: Float!Non-null floating-point number
QueryRoot type for read operations
movie(id: ID!)Returns one movie by ID
movies: [Movie!]!Returns a non-null list of non-null movies

The ! means non-null - the API guarantees a value.


Type System Overview

┌─────────────────────────────────────────────────────────────────────┐
│ GRAPHQL TYPE SYSTEM │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Scalar Types Int, Float, String, Boolean, ID, custom │
│ Object Types Movie, Review, User (domain entities) │
│ Enum Types Fixed set of values (NOMINATED, WON) │
│ Interface Types Abstract types with shared fields │
│ Union Types One of several possible types │
│ Input Types Structured arguments for mutations │
│ List Types Arrays: [Movie], [String!]! │
│ Non-Null Types Guaranteed values: String! │
│ │
└─────────────────────────────────────────────────────────────────────┘

Scalar Types

Scalars are primitive types that resolve to a single value. They are the leaf nodes of a query.

Built-in Scalars

ScalarDescription
IntSigned 32-bit integer
FloatSigned double-precision floating-point
StringUTF-8 character sequence
Booleantrue or false
IDUnique identifier (serialized as String)

Custom Scalars

You can define custom scalars for domain-specific values:

scalar DateTime
scalar Email
scalar URL

The server implementation defines how these are serialized and validated.


Object Types

Object types represent domain entities. They contain fields that can be scalars, other objects, enums, or lists.

type Movie {
id: ID!
title: String!
releaseYear: Int!
director: Director! # Relationship to another object
reviews: [Review!]! # List of objects
genre: Genre! # Enum
}

Arguments

Any field can accept arguments:

type Movie {
id: ID!
title: String!
duration(unit: TimeUnit = MINUTE): Float!
reviews(limit: Int = 10, sortBy: ReviewSort): [Review!]!
}
AspectExampleMeaning
Named argumentsunit: TimeUnitPassed by name
Default values= MINUTEUsed if argument not provided
RequiredNo = and non-null typeMust be provided

Enum Types

Enums represent a fixed, known set of values:

enum AwardStatus {
NOMINATED
WON
}

enum Genre {
ACTION
COMEDY
DRAMA
SCIENCE_FICTION
}
┌─────────────────────────────────────────────────────────────────────┐
│ ENUM BEST PRACTICES │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ✅ Use enums for stable, closed sets │
│ NOMINATED, WON, PENDING │
│ │
│ ❌ Avoid enums for frequently changing values │
│ Country codes, category names (use String instead) │
│ │
│ Why? Adding enum values is safe, but removing them breaks clients. │
│ │
└─────────────────────────────────────────────────────────────────────┘

Interface Types

Interfaces define a set of fields that implementing types must include:

interface Content {
id: ID!
title: String!
releaseYear: Int!
}

type Movie implements Content {
id: ID!
title: String!
releaseYear: Int!
duration: Int! # Additional field
}

type TvShow implements Content {
id: ID!
title: String!
releaseYear: Int!
seasons: Int! # Additional field
}

Interfaces enable polymorphic queries:

query {
search(query: "matrix") {
title # Works for any Content
... on Movie {
duration # Only for Movies
}
... on TvShow {
seasons # Only for TvShows
}
}
}

Note: Interface fields must be repeated in implementing types. This was a deliberate design choice for readability.


Union Types

Unions group types that share no common fields:

union SearchResult = Movie | Review | Award

Unlike interfaces, union members don't need shared fields:

query {
search(query: "oscar") {
... on Movie {
title
releaseYear
}
... on Review {
rating
comment
}
... on Award {
name
category
}
}
}

Union members must be concrete object types (not interfaces or other unions).


Input Types

Input types define structured arguments, especially for mutations:

input CreateMovieInput {
title: String!
releaseYear: Int!
genre: Genre!
directorId: ID!
}

input UpdateMovieInput {
title: String
releaseYear: Int
genre: Genre
}

type Mutation {
createMovie(input: CreateMovieInput!): Movie!
updateMovie(id: ID!, input: UpdateMovieInput!): Movie
}
┌─────────────────────────────────────────────────────────────────────┐
│ INPUT vs OUTPUT TYPES │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Input types (input): Used for arguments │
│ Output types (type): Used for responses │
│ │
│ They cannot be mixed - a type cannot be used as an input. │
│ │
└─────────────────────────────────────────────────────────────────────┘

Operation Types

GraphQL defines three operation types:

Query (Read Operations)

Every GraphQL schema must have a Query type:

type Query {
movie(id: ID!): Movie
movies(limit: Int = 20): [Movie!]!
searchMovies(query: String!): [Movie!]!
}

Mutation (Write Operations)

Mutations modify data and return results:

type Mutation {
createMovie(input: CreateMovieInput!): Movie!
updateMovie(id: ID!, input: UpdateMovieInput!): Movie
deleteMovie(id: ID!): Boolean!
}

Subscription (Real-Time Operations)

Subscriptions deliver data when events occur:

type Subscription {
movieAdded: Movie!
reviewPosted(movieId: ID!): Review!
}

Nullability

The ! suffix means a field is non-null:

type Movie {
id: ID! # Never null
title: String! # Never null
sequel: Movie # May be null (not all movies have sequels)
rating: Float # May be null (not yet rated)
}

List Nullability

type Movie {
# ✅ Recommended: Non-null list of non-null items
actors: [Actor!]! # Always a list, items never null

# Nullable list of non-null items
nominations: [Award!] # null = "unknown", [] = "none"

# Non-null list of nullable items (rare)
tags: [String]! # Always a list, items may be null
}

Documentation

GraphQL supports inline documentation that appears in introspection:

"""
A movie available in the catalog.
Contains details about films including ratings and reviews.
"""
type Movie {
"Unique identifier for the movie"
id: ID!

"""
The movie's display title.
For international films, this may differ from originalTitle.
"""
title: String!

"Average user rating (0-10 scale)"
rating: Float
}

Comments vs Descriptions

# This is a comment  -  ignored by GraphQL, not visible in introspection
"""This is a description - visible in introspection and tooling"""
type Movie {
id: ID!
}

Schema Definition

You can explicitly define root operation types:

schema {
query: Query
mutation: Mutation
subscription: Subscription
}

This is optional when using the default names (Query, Mutation, Subscription).


Best Practices

┌─────────────────────────────────────────────────────────────────────┐
│ SCHEMA DESIGN TIPS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ✅ Prefer object relationships over foreign keys │
│ director: Director! instead of directorId: ID! │
│ │
│ ✅ Use ID! for identifiers │
│ Better for caching and client libraries │
│ │
│ ✅ Default to non-null, use nullable intentionally │
│ Only allow null when it has meaning │
│ │
│ ✅ Use [Thing!]! for collections │
│ Non-null list of non-null items │
│ │
│ ✅ Document with triple-quote descriptions │
│ Visible in introspection and tooling │
│ │
│ ✅ Start simple, add complexity later │
│ Pagination, filtering, etc. can be added as needed │
│ │
└─────────────────────────────────────────────────────────────────────┘

Summary

ConceptDescription
SchemaContract defining types, fields, and operations
SDLSchema Definition Language for writing schemas
ScalarsPrimitive types (Int, String, Boolean, Float, ID)
Object typesDomain entities with fields
EnumsFixed set of values
InterfacesAbstract types with shared fields
UnionsOne of several possible types
Input typesStructured arguments for mutations
Non-null (!)Field guaranteed to have a value

What's Next?

In the next chapter, we'll explore Queries in Depth - variables, fragments, aliases, directives, and advanced query patterns.