GraphQL

GraphQL Schema and Type System

20 min Lesson 2 of 35

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.