GraphQL Types
Types are the foundation of GraphQL's type system. They define the shape of your data and ensure that queries return consistent, predictable results.
GraphQL has several built-in scalar types and lets you create your own custom types.
Scalar Types
Scalar types are the primitive types that represent single values. GraphQL has five built-in scalar types:
type Example {
id: ID! // Unique identifier (string or int)
name: String! // UTF-8 string
age: Int // 32-bit integer
price: Float! // Double-precision floating-point
isActive: Boolean! // true or false
}
The ID type is special - it's used for unique identifiers and can be serialized as either a string or an integer. The ! after a type means it's non-nullable (required).
Object Types
Object types are the most common types you'll create. They represent a collection of fields, like a User or a Post.
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
tags: [String!]!
}
Notice how types can reference other types. A User has posts (an array of Post), and a Post has an author (a single User). This creates a graph of connected data.
List and Non-Null Types
GraphQL lets you modify types with list and non-null modifiers:
type Example {
required: String! // Non-nullable string
optional: String // Nullable string (can be null)
requiredList: [String!]! // Non-nullable list of non-nullable strings
optionalList: [String!] // Nullable list, but items are non-nullable
nullableItems: [String] // Nullable list with nullable items
}
// What this means:
// String! → Never null
// String → Can be null
// [String!]! → List is non-null, items are non-null
// [String!] → List can be null, but items can't
// [String] → Both list and items can be null
Getting these modifiers right is important for error handling and client expectations.
Input Types
When you need to pass complex data to mutations, you use input types. They're similar to object types but designed for input.
input CreateUserInput {
name: String!
email: String!
age: Int
bio: String
}
input UpdateUserInput {
name: String
email: String
bio: String
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
Input types keep your mutations clean and organized. Instead of having many parameters, you group related data into a single input object.
Custom Types with Enums
Enums restrict a field to a set of predefined values. They're great for fields that have a fixed set of options.
enum Role {
ADMIN
USER
MODERATOR
}
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
type User {
id: ID!
name: String!
role: Role!
}
type Post {
id: ID!
title: String!
status: PostStatus!
}
Enums make your API more type-safe and self-documenting. Clients know exactly what values are valid for each field.