Skip to main content

Multi-Hop Traversal

The thing GraphQL is uniquely good at: walk multiple edges of the graph in one request. Instead of "fetch project → fetch its license → fetch other projects with that license" as three round-trips, you ask the whole thing in one query and the server traverses for you.

Example — find sibling projects under the same license

"What other projects use the same software license as ONNX Runtime?"

query SiblingProjectsByLicense($id: UUID!) {
entity(id: $id) {
name
relations(
first: 5
filter: { typeEntity: { name: { is: "Software licenses" } } }
) {
nodes {
toEntity {
id
name
backlinks(
first: 10
filter: { typeEntity: { name: { is: "Software licenses" } } }
) {
nodes {
fromEntity { id name }
}
}
}
}
}
}
}
{ "id": "012b60596695419fa016590746e5a243" }

Response

{
"data": {
"entity": {
"name": "ONNX Runtime",
"relations": {
"nodes": [
{
"toEntity": {
"id": "aad29168dd2f4f7c825504835850826c",
"name": "MIT",
"backlinks": {
"nodes": [
{ "fromEntity": { "id": "1adca1849d414724a1e0ff4a2fead9ad", "name": "Weights & Biases" } },
{ "fromEntity": { "id": "012b60596695419fa016590746e5a243", "name": "ONNX Runtime" } }
]
}
}
}
]
}
}
}
}

Two hops in one round-trip:

  1. Hop 1: ONNX Runtime → its Software licenses relation → MIT
  2. Hop 2: MIT → its inverse Software licenses relations → other projects (Weights & Biases, etc.)
Loading interactive query runner…

When to use multi-hop

  • "Sibling" queries — projects sharing a license, papers sharing an author, entities sharing a topic
  • Rendering connected views — show the entity + its 1-hop neighbors + each neighbor's most-relevant relations
  • "Reachability" checks — is entity A connected to entity B within N hops?
  • Schema introspection at the data layer — what types use this property?

Filtering at each level

The filter argument on relations and backlinks accepts the same fields as a RelationFilter, so you can scope each hop:

relations(
first: 5
filter: {
typeEntity: { name: { is: "Topics" } } # only Topics edges
}
)

You can also filter the target entity directly:

relations(
first: 5
filter: {
toEntity: { typeIds: { anyEqualTo: $PERSON_TYPE_ID } } # only relations to People
}
)

Performance notes

  • Each level multiplies the request size. Keep first small at deeper levels (5-10), or you can blow up the response payload — a query asking for 50 → 50 → 50 means 125,000 nodes returned.

  • The server fans out queries internally. Multi-hop is faster than separate round-trips because each level shares a database connection, but it's not free; very deep traversals can timeout.

  • GraphQL fragments help readability when you walk the same shape multiple times:

    fragment EntityCard on Entity {
    id
    name
    }

    Then fromEntity { ...EntityCard } everywhere.

Notes

  • backlinks is the inverse of relations — same filter shape, just walks the edge in the other direction.
  • null names happen at every level — block entities, anonymous entities, or partial publishes. Filter them out client-side or in the filter argument.
  • Use named operations and variables, never string interpolation, especially when the query has multiple variables.