Skip to content

Graph view endpoints

Graph view endpoints return typed projections of the course universe. There is no GraphQL in v1 (ADR 0009); the seven named views below are the v1 graph contract.

Each is POST, accepts a request describing the projection bounds, and returns a typed graph_view payload inside the standard envelope.

The list lives at GET /api/v1/graph/views for client discovery; the projections themselves are:

EndpointPurpose
POST /api/v1/graph/views/course-neighborhoodLocal neighborhood around one course.
POST /api/v1/graph/views/course-universeThe full universal projection used by the Canva.
POST /api/v1/graph/views/course-pathwaysPaths from one course (or set of courses) to a target.
POST /api/v1/graph/views/credential-requirementsTree projection of a credential’s requirements as a graph.
POST /api/v1/graph/views/unlock-overlayOverlay of unlocked / taken / planned statuses on a given graph view. State-bearing.
POST /api/v1/graph/views/target-relevanceFilter a graph view to only nodes relevant to a target credential or course. State-bearing.
POST /api/v1/graph/views/expand-nodeExpand one node’s full neighborhood at higher detail without changing the global layout.

Bounds

Every graph view request takes optional bound parameters; defaults and hard maxima:

BoundDefaultHard max
max_nodes2502,500
max_edges6007,500
max_depth24
max_courses1002,500
max_credentials525

These exist because uwwoe runs on a single Fly machine; producing unbounded responses would be a DoS vector. Bounds are also a UX feature — a 50,000-edge response is unreadable.

Truncation, not error

  • Request bound ≤ default → response is complete.
  • Request bound > default but ≤ hard max → response includes graph_view_truncated in warnings, with an omitted count in meta. Status 200 OK.
  • Request bound > hard max → 400 bad_request with code bound_exceeds_hard_max.

Request shape (shared)

{
"bounds": {
"max_nodes": 500,
"max_depth": 3
},
"filter": { ... }, // endpoint-specific
"include_layout": true // whether to bundle precomputed positions
}

Including layout is recommended for full-universe views — server-side layout is precomputed at index build time per ADR 0026, so requesting it adds no compute cost.

Response shape (shared)

{
"data": {
"nodes": [ { "id": "course:MATH 247", "kind": "course", "...": "..." } ],
"edges": [ { "from": "course:MATH 137", "to": "course:MATH 247", "relation": "prerequisite" } ],
"layout": { ... }, // present if requested and available
"view_meta": { "omitted_nodes": 12, "omitted_edges": 45 }
},
"meta": { ... },
"warnings": [
{ "code": "graph_view_truncated", "message": "Truncated to max_nodes=500" }
],
"unknowns": [],
"source_references": [ ... ]
}

State-bearing views

unlock-overlay and target-relevance require an Authorization header. They combine the graph projection with the user’s state to overlay statuses (taken / planned / unlocked) or to filter to target-relevant nodes.

Atlas worker payloads do not carry the state token — the worker receives only the resulting filtered/overlaid nodes and edges, not the state itself. Gate 4 enforces this.

Want more?