Skip to main content

Fundamentals

Comprehensive guide to GraphQL concepts, schema design, and query language fundamentals.

GraphQL Overview

What is GraphQL?

GraphQL is a query language and runtime for APIs that allows clients to request exactly the data they need. Unlike REST APIs that expose multiple endpoints, GraphQL exposes a single endpoint with a flexible query language.

Key Benefits

  • Single endpoint - One URL for all data operations
  • Flexible queries - Clients request exactly what they need
  • Strong typing - Schema defines all available operations
  • Introspection - Schema is self-documenting
  • Real-time subscriptions - Built-in support for live updates

Schema Definition Language (SDL)

Basic Types

# Scalar types
scalar DateTime
scalar JSON
scalar Upload

# Object types
type User {
id: ID! # Non-null ID
name: String! # Non-null string
email: String!
age: Int # Nullable integer
isActive: Boolean! # Non-null boolean
balance: Float # Nullable float
createdAt: DateTime!
metadata: JSON
}

# List types
type Post {
id: ID!
title: String!
tags: [String!]! # Non-null list of non-null strings
comments: [Comment!] # Nullable list of non-null comments
categories: [Category] # Nullable list of nullable categories
}

# Enum types
enum UserRole {
ADMIN
MODERATOR
USER
GUEST
}

enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}

# Interface types
interface Node {
id: ID!
createdAt: DateTime!
}

interface Content {
title: String!
body: String!
author: User!
}

# Union types
union SearchResult = User | Post | Comment

# Input types
input UserInput {
name: String!
email: String!
age: Int
role: UserRole = USER # Default value
}

input PostInput {
title: String!
content: String!
status: PostStatus = DRAFT
tags: [String!]
categoryIds: [ID!]
}

Schema Structure

# Root operation types
type Query {
# Single object queries
user(id: ID!): User
post(id: ID!): Post

# List queries
users(
limit: Int = 10
offset: Int = 0
filter: UserFilter
sort: UserSort
): [User!]!

# Search queries
search(query: String!): [SearchResult!]!

# Aggregation queries
userStats: UserStats!
}

type Mutation {
# Create operations
createUser(input: UserInput!): User!
createPost(input: PostInput!): Post!

# Update operations
updateUser(id: ID!, input: UserInput!): User!
updatePost(id: ID!, input: PostInput!): Post!

# Delete operations
deleteUser(id: ID!): Boolean!
deletePost(id: ID!): Boolean!

# Batch operations
createUsers(inputs: [UserInput!]!): [User!]!
updateUsers(updates: [UserUpdateInput!]!): [User!]!
}

type Subscription {
# Real-time updates
userCreated: User!
postUpdated(id: ID!): Post!
commentAdded(postId: ID!): Comment!

# Room-based subscriptions
chatMessage(roomId: ID!): ChatMessage!
}

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

Query Language

Basic Queries

# Simple field selection
query {
user(id: "1") {
name
email
}
}

# Nested queries
query {
user(id: "1") {
name
email
posts {
title
content
comments {
text
author {
name
}
}
}
}
}

# Multiple queries in one request
query {
user1: user(id: "1") {
name
email
}
user2: user(id: "2") {
name
email
}
allUsers: users {
id
name
}
}

Query Variables

# Query with variables
query GetUser($userId: ID!, $includeComments: Boolean = false) {
user(id: $userId) {
name
email
posts {
title
content
comments @include(if: $includeComments) {
text
author {
name
}
}
}
}
}

# Variables JSON
{
"userId": "1",
"includeComments": true
}

Fragments

# Fragment definition
fragment UserInfo on User {
id
name
email
role
createdAt
}

# Using fragments
query {
user(id: "1") {
...UserInfo
posts {
title
author {
...UserInfo
}
}
}
}

# Inline fragments (for unions/interfaces)
query {
search(query: "graphql") {
... on User {
name
email
}
... on Post {
title
content
}
... on Comment {
text
author {
name
}
}
}
}

Directives

# Built-in directives
query GetUser($includeEmail: Boolean!, $skipPosts: Boolean!) {
user(id: "1") {
name
email @include(if: $includeEmail)
posts @skip(if: $skipPosts) {
title
content
}
}
}

# Custom directives
directive @auth(requires: UserRole = USER) on FIELD_DEFINITION
directive @deprecated(
reason: String = "No longer supported"
) on FIELD_DEFINITION
directive @rate_limit(max: Int!, window: Int!) on FIELD_DEFINITION

# Schema with directives
type User {
id: ID!
name: String!
email: String! @auth(requires: USER)
adminNotes: String @auth(requires: ADMIN)
oldField: String @deprecated(reason: "Use newField instead")
}

type Query {
users: [User!]! @rate_limit(max: 100, window: 60)
sensitiveData: String! @auth(requires: ADMIN)
}

Mutations

Basic Mutations

# Create mutation
mutation CreateUser($input: UserInput!) {
createUser(input: $input) {
id
name
email
createdAt
}
}

# Variables
{
"input": {
"name": "John Doe",
"email": "john@example.com",
"role": "USER"
}
}

# Update mutation
mutation UpdateUser($id: ID!, $input: UserInput!) {
updateUser(id: $id, input: $input) {
id
name
email
updatedAt
}
}

# Delete mutation
mutation DeleteUser($id: ID!) {
deleteUser(id: $id)
}

Batch Mutations

# Multiple mutations in one request
mutation {
createUser(input: { name: "Alice", email: "alice@example.com" }) {
id
name
}
createPost(input: { title: "Hello", content: "World" }) {
id
title
}
}

# Batch create
mutation CreateUsers($inputs: [UserInput!]!) {
createUsers(inputs: $inputs) {
id
name
email
}
}

Optimistic Updates

# Mutation with optimistic response
mutation UpdatePost($id: ID!, $input: PostInput!) {
updatePost(id: $id, input: $input) {
id
title
content
updatedAt
version # For optimistic concurrency control
}
}

Subscriptions

Real-time Subscriptions

# Basic subscription
subscription {
commentAdded {
id
text
author {
name
}
post {
title
}
}
}

# Subscription with variables
subscription CommentAdded($postId: ID!) {
commentAdded(postId: $postId) {
id
text
author {
name
}
}
}

# Multiple subscriptions
subscription {
userOnline {
id
name
status
}
messageReceived {
id
content
sender {
name
}
}
}

Advanced Schema Patterns

Interfaces and Polymorphism

# Interface definition
interface Node {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
}

interface Searchable {
title: String!
content: String!
}

# Implementing interfaces
type User implements Node {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
name: String!
email: String!
}

type Post implements Node & Searchable {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
title: String!
content: String!
author: User!
}

# Query with interfaces
query {
nodes {
id
createdAt
... on User {
name
email
}
... on Post {
title
content
}
}
}

Union Types

# Union definition
union FeedItem = Post | Comment | User

# Query with union
query {
feed {
... on Post {
title
content
author {
name
}
}
... on Comment {
text
author {
name
}
post {
title
}
}
... on User {
name
email
}
}
}

Relay Specification

# Node interface (Relay)
interface Node {
id: ID!
}

# Connection types (Relay)
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}

type UserEdge {
node: User!
cursor: String!
}

type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}

# Pagination query
query GetUsers($first: Int, $after: String) {
users(first: $first, after: $after) {
edges {
node {
id
name
email
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}

Error Handling

Error Types

# Custom error types
type ValidationError {
field: String!
message: String!
}

type AuthenticationError {
message: String!
code: String!
}

# Result types with errors
type UserResult {
success: Boolean!
user: User
errors: [ValidationError!]
}

type Mutation {
createUser(input: UserInput!): UserResult!
}

Error Queries

# Query with error handling
mutation CreateUser($input: UserInput!) {
createUser(input: $input) {
success
user {
id
name
email
}
errors {
field
message
}
}
}

# Response with errors
{
"data": {
"createUser": {
"success": false,
"user": null,
"errors": [
{
"field": "email",
"message": "Email is already taken"
}
]
}
}
}

Schema Documentation

Documentation Strings

"""
Represents a user in the system.
Users can create posts and comments.
"""
type User {
"Unique identifier for the user"
id: ID!

"User's display name"
name: String!

"User's email address (private)"
email: String!

"""
User's role in the system.
Determines access permissions.
"""
role: UserRole!

"Posts created by this user"
posts: [Post!]!

"Comments made by this user"
comments: [Comment!]!
}

"""
Input for creating a new user.
All fields are validated before creation.
"""
input UserInput {
"Display name (3-50 characters)"
name: String!

"Valid email address"
email: String!

"User role (defaults to USER)"
role: UserRole = USER
}

Introspection

# Schema introspection
query {
__schema {
types {
name
description
fields {
name
type {
name
}
description
}
}
}
}

# Type introspection
query {
__type(name: "User") {
name
description
fields {
name
type {
name
kind
}
description
}
}
}

Best Practices

Schema Design

# Good: Descriptive names
type User {
id: ID!
displayName: String!
emailAddress: String!
registrationDate: DateTime!
}

# Bad: Unclear names
type User {
id: ID!
name: String!
email: String!
date: DateTime!
}

# Good: Consistent naming
type Query {
user(id: ID!): User
users(filter: UserFilter): [User!]!
usersByRole(role: UserRole!): [User!]!
}

# Bad: Inconsistent naming
type Query {
user(id: ID!): User
allUsers: [User!]!
getUsersByRole(role: UserRole!): [User!]!
}

Input Validation

# Input with validation constraints
input UserInput {
"Display name (3-50 characters)"
name: String! @constraint(minLength: 3, maxLength: 50)

"Valid email address"
email: String! @constraint(format: "email")

"Age must be between 13 and 120"
age: Int @constraint(min: 13, max: 120)

"Password (minimum 8 characters)"
password: String! @constraint(minLength: 8)
}

Performance Considerations

# Pagination for large datasets
type Query {
users(
first: Int = 10
after: String
filter: UserFilter
sort: UserSort
): UserConnection!
}

# Limiting query depth
type Query {
user(id: ID!): User # Max depth: 10
}

# Field-level caching
type User {
id: ID!
name: String!
posts: [Post!]! @cacheControl(maxAge: 300)
profileImage: String @cacheControl(maxAge: 3600)
}

This comprehensive guide covers GraphQL fundamentals from basic schema definition to advanced patterns and best practices. Use these concepts to build efficient and maintainable GraphQL APIs.