Understanding the GraphQL Schema
The GraphQL schema is the contract between the client and server. It defines what queries clients can make, what types of data can be fetched, and the relationships between types. Every GraphQL service defines a schema that completely describes the set of possible data you can query on that service.
Key Concept: The schema is written using Schema Definition Language (SDL), a human-readable syntax for defining GraphQL schemas.
Schema Definition Language (SDL)
SDL is a simple, intuitive language for defining your GraphQL schema. Here's a basic example:
type User {
id: ID!
name: String!
email: String!
age: Int
isActive: Boolean!
}
type Query {
user(id: ID!): User
users: [User!]!
}
This schema defines a User type with several fields and a Query type with two query operations.
Scalar Types
GraphQL comes with a set of default scalar types that represent primitive values:
- Int: A signed 32-bit integer (e.g., 42, -10)
- Float: A signed double-precision floating-point value (e.g., 3.14, -0.5)
- String: A UTF-8 character sequence (e.g., "Hello", "مرحباً")
- Boolean: true or false
- ID: A unique identifier, serialized as a String but not intended for human reading
type Product {
id: ID! # Unique identifier
name: String! # Product name
price: Float! # Price as decimal
quantity: Int! # Quantity in stock
inStock: Boolean! # Availability status
}
Best Practice: Use ID type for unique identifiers even though they're serialized as strings. This communicates intent and allows GraphQL clients to cache and normalize data more effectively.
Object Types
Object types are the most common type you'll define in your schema. They represent a collection of fields, where each field has a specific type:
type Author {
id: ID!
name: String!
email: String!
books: [Book!]!
}
type Book {
id: ID!
title: String!
isbn: String
publishedYear: Int!
author: Author!
}
Notice how Author and Book reference each other, creating a relationship between the two types.
Enum Types
Enums are special scalar types that restrict a field to a particular set of allowed values:
enum Role {
ADMIN
USER
MODERATOR
GUEST
}
enum OrderStatus {
PENDING
PROCESSING
SHIPPED
DELIVERED
CANCELLED
}
type User {
id: ID!
name: String!
role: Role!
}
type Order {
id: ID!
status: OrderStatus!
total: Float!
}
Important: Enum values are serialized as strings in JSON, but GraphQL validates that only defined enum values are used.
List Types
Lists represent an ordered sequence of values. They're denoted by wrapping a type in square brackets:
type User {
id: ID!
name: String!
hobbies: [String!]! # List of strings
friends: [User!]! # List of User objects
tags: [String] # Nullable list with nullable items
}
type Query {
users: [User!]! # Returns array of users
searchUsers(query: String!): [User] # May return null or empty array
}
Non-Null Type Modifier
By default, all types in GraphQL are nullable. The exclamation mark (!) makes a type non-nullable:
type User {
id: ID! # Cannot be null
name: String! # Cannot be null
email: String # Can be null
age: Int # Can be null
friends: [User!]! # Array cannot be null, items cannot be null
tags: [String!] # Array can be null, but items cannot be null
posts: [Post]! # Array cannot be null, but items can be null
comments: [Comment] # Both array and items can be null
}
Caution: Be careful with non-null modifiers on lists. [User!]! means the list cannot be null AND no item can be null. If any item is null, the entire field will be null.
Type Modifiers Combinations
Understanding the different combinations of list and non-null modifiers is crucial:
type Examples {
# Can return: null, [], ["a", "b"], ["a", null]
example1: [String]
# Can return: [], ["a", "b"], ["a", null]
# Cannot return: null
example2: [String]!
# Can return: null, [], ["a", "b"]
# Cannot return: ["a", null]
example3: [String!]
# Can return: [], ["a", "b"]
# Cannot return: null, ["a", null]
example4: [String!]!
}
Input Types
Input types are special object types used for passing complex objects as arguments:
input CreateUserInput {
name: String!
email: String!
age: Int
role: Role!
}
input UpdateUserInput {
name: String
email: String
age: Int
role: Role
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
Best Practice: Use input types for mutations instead of listing many individual arguments. This makes your schema more maintainable and easier to evolve.
Interface Types
Interfaces allow you to define a set of fields that multiple types must include:
interface Node {
id: ID!
createdAt: String!
}
type User implements Node {
id: ID!
createdAt: String!
name: String!
email: String!
}
type Post implements Node {
id: ID!
createdAt: String!
title: String!
content: String!
author: User!
}
type Query {
node(id: ID!): Node
}
Union Types
Union types allow a field to return one of multiple object types:
union SearchResult = User | Post | Comment
type Query {
search(query: String!): [SearchResult!]!
}
# In a query, you use fragments to access fields:
query {
search(query: "graphql") {
... on User {
name
email
}
... on Post {
title
author {
name
}
}
... on Comment {
text
author {
name
}
}
}
}
Exercise: Design a schema for a simple blog system with the following requirements:
- Users can write posts and comments
- Posts have a title, content, status (DRAFT, PUBLISHED, ARCHIVED), and tags
- Comments belong to posts and have text and optional rating (1-5)
- Include appropriate queries and mutations
- Use proper type modifiers for nullable and non-nullable fields
Summary
In this lesson, you've learned about the GraphQL type system, including scalar types, object types, enums, lists, and type modifiers. The schema is the foundation of your GraphQL API - it defines the contract between client and server and enables powerful tooling like auto-completion and validation.