Skip to main content

Graph Traversal (Full vs Leaf Types)

The Massdriver GraphQL API uses a Rooted Traversal Pattern to ensure predictable, bounded queries while still allowing flexible graph exploration.

Core Concept

Each entity has two GraphQL types:

  • Full type (e.g., Project) - Returned by getter queries, allows deep traversal
  • Leaf type (e.g., ProjectLeaf) - Returned by list queries, terminal (no further traversal)

The Rules

Query TypeReturnsCan Traverse Deeper?
Getter (project)Full type✅ Yes
List (projects)Leaf types❌ No (terminal)
Single resource from Full (e.g., environment.project)Full type✅ Yes
List of resources from Full (e.g., project.environments)Leaf types❌ No (terminal)
Any field from LeafLeaf type❌ No (terminal)

Examples

List Query (Returns Leaves - Terminal)

query {
environments(organizationId: "acme") {
items {
id
name
project { # Returns ProjectLeaf
id
name
# ❌ NO environments field - it's a leaf!
}
}
}
}

Getter Query (Returns Full - Can Traverse)

query {
environment(organizationId: "acme", id: "myproj-staging") {
id
name
project { # Returns Project (full type)
id
name
environments { # ✅ Can access list because project is full type
items {
id
project { # But this is ProjectLeaf - terminal!
id
}
}
}
}
}
}

Why This Pattern?

Bounded Queries

Making list items terminal keeps queries predictable and fast.

Explicit Depth

When you need details from a list item, make a separate getter call for that specific resource.

Practical Pattern

When you need to traverse deeply from a list:

# Step 1: Get the list
query {
environments(organizationId: "acme") {
items {
id # Collect IDs you want to explore
name
}
}
}

# Step 2: Get full details for specific items
query {
environment(organizationId: "acme", id: "myproj-staging") {
id
name
project {
environments {
items { id name }
}
}
packages {
items { id name status }
}
}
}

Type Naming Convention

Full TypeLeaf Type
ProjectProjectLeaf
EnvironmentEnvironmentLeaf
PackagePackageLeaf
DeploymentDeploymentLeaf

The Leaf suffix indicates the type is terminal and cannot be used for further graph exploration.