Loom Cosmos Gremlin Graph Editor — Fabric-parity spec¶
Captured 2026-05-26 by catalog agent. Sources: Microsoft Learn — What is Azure Cosmos DB for Apache Gremlin?, Gremlin TinkerPop compatibility, Execute queries on graph data, Graph data modeling, Gremlin limits, Gremlin FAQ, Quickstart console. Cross-checked against current Loom editor at
apps/fiab-console/lib/editors/graph-editors.tsx::CosmosGremlinGraphEditorand BFF routeapps/fiab-console/app/api/items/cosmos-gremlin-graph/[id]/query/route.ts.
What it is¶
Azure Cosmos DB for Apache Gremlin is a managed property-graph database. It stores vertices and edges with arbitrary key/value properties, partitions horizontally across an Azure Cosmos account, and speaks the Apache TinkerPop Gremlin traversal language over a WebSocket endpoint shaped wss://<account>.gremlin.cosmos.azure.com:443/. There is no native Microsoft "graph data explorer" portal blade equivalent to the SQL data explorer for the Gremlin API — Microsoft documents partner tools (Linkurious, yWorks, Graphistry, Cambridge Intelligence, Hackolade) for visualization, plus the open-source tinkerpop/gremlin-console Docker image for ad-hoc traversal. Our Fabric-parity target is therefore the portal Data Explorer surface that Cosmos DB does ship for the Gremlin API (graph + database tree, query editor with results in graph/JSON/table tabs, and the executionProfile() viewer).
UI components¶
Page chrome¶
- Title bar: graph name + saved-state indicator
- Standard Cosmos account global bar (settings, notifications, help, account)
- Top toolbar: New Vertex, New Edge, Execute Gremlin Query, Save Query, Load Query, Save Results, Settings
Left pane — Database tree¶
- Tree of
cosmosdb account → database → graphhierarchy - Per-graph actions: New Graph Query, New Items (vertex), Settings (throughput, partition key path, indexing policy)
- Per-database actions: New Graph (with
partition-key-path+ throughput input) - Stored procedures / UDFs / triggers branches (Cosmos primitives; present in the Gremlin explorer because the underlying engine is shared with NoSQL)
Query editor pane (top main)¶
- Monaco-based Gremlin editor with TinkerPop syntax highlighting
- Inline help link to the supported Gremlin steps table
- Execute Gremlin Query button
- Save Query / Load Query (named queries persisted per account)
- Query parameterization (dictionary of bindings — required to prevent injection per Gremlin FAQ)
- Hard limits surfaced inline: script length 64 KB, operator depth 400, traversal timeout 30 s, repeat limit 32
Results pane (bottom main) — three tabs¶
- Graph — interactive force-directed visualization of returned vertices + edges, with right-click expand (
out()/in()/both()) on a selected vertex, hide/show by label, pin/unpin, zoom-on-node - JSON — raw response (vertices, edges, paths, scalars)
- Table — flattened columns for projected properties (works when the traversal ends in
.values(...),.valueMap(),.project(...).by(...)) - Query Stats — request-charge (RUs), round-trip latency, retrieved document count, server-side execution time
Execution profile viewer¶
- Surfaces output of
.executionProfile()(appended to a traversal) - Step-by-step breakdown with
fanoutFactor, working-set size, time per step - Highlights blind fan-out patterns (high fanoutFactor without partition-key predicate)
Vertex / edge form editor¶
- New Vertex dialog: label + partition-key value + repeating property rows (name/value/type)
- New Edge dialog: source vertex id, target vertex id, label, repeating property rows
- Triggers
g.addV(...)/g.addE(...)traversals; closes on success
Settings drawer (per graph)¶
- Throughput (RU/s; manual or autoscale)
- Partition key path (read-only post-create)
- Indexing policy editor (JSON; consistent vs none, included/excluded paths)
- Time-to-live (item TTL + default container TTL)
- Conflict resolution (last-writer-wins vs custom merge)
- Geo-replication add/remove regions
What Loom has¶
The current CosmosGremlinGraphEditor (apps/fiab-console/lib/editors/graph-editors.tsx, lines 94-142) is partially functional:
- Endpoint input bound to
NEXT_PUBLIC_LOOM_COSMOS_GREMLIN_ENDPOINT - Plain
<textarea>Gremlin editor seeded withSAMPLE_GREMLIN(4-lineg.V().hasLabel('person')...example) - Run button POSTing to
/api/items/cosmos-gremlin-graph/[id]/querywhich callsexecuteGremlin()inlib/azure/gremlin-client.ts - BFF returns 501 with
deferred=truewhenLOOM_COSMOS_GREMLIN_ENDPOINTis unset or thegremlinnpm package isn't installed; UI renders the deferred MessageBar honestly - Results render as a
<pre>JSON block (no graph / table / stats tabs) - Ribbon advertises Edges / Vertices ribbon actions but they are not wired
- Cosmos persistence of the Gremlin script + endpoint via the generic item-state pattern
- Grade: C (functional but rough) — real traversal execution works end-to-end when env is set, but every visualization and authoring surface is missing
Gaps for parity¶
- Graph visualization tab absent — no force-directed canvas, no node expand, no label-based filter. Loom dumps JSON. The single largest parity gap.
- Table tab absent — flattened-columns view for
valueMap()/project()traversals is missing. - Query Stats tab absent — RU charge, retrieved-doc count, server-side time are not surfaced even though they're in every Cosmos response header.
- executionProfile() viewer absent — no per-step breakdown, no fanoutFactor warning, no blind-fan-out flagging.
- Monaco editor + Gremlin syntax highlighting — plain
<textarea>with no IntelliSense, no.V()/.E()/.out()/.in()/.has()completion. - Database tree navigator absent — no
account → database → graphtree, no per-graph Settings, no New Graph dialog. - New Vertex / New Edge form editors absent — every mutation must be typed as Gremlin in the textarea.
- Indexing-policy + throughput + TTL editors absent — Cosmos primitives that the portal exposes but Loom doesn't.
- Saved queries absent — no per-account query library; only the current textarea persists into item state.
- Parameterized queries absent — Gremlin FAQ explicitly recommends parameter dictionaries to prevent injection; Loom takes a raw string.
- Limit surfacing absent — the 64 KB / 400-operator / 30-s / repeat-32 limits aren't shown in the editor UI; long queries fail with opaque 429s.
- AAD auth path absent —
gremlin-client.tsdefaults to account-key auth; Service Connector pattern for managed-identity-to-listKeys is documented but not wired. - Ribbon advertises Edges / Vertices buttons that emit nothing — vaporware-rule violation; either wire them to
g.V().limit(50)/g.E().limit(50)shortcuts or remove the labels. - No partition-key reminder in the editor — graph modeling docs hammer "include partition key in V() predicate" as the #1 performance lever; Loom should surface a Caption1 hint inline.
Backend mapping¶
| Loom surface | Backing service | Notes |
|---|---|---|
| Graph + database + indexing-policy persistence | Azure Cosmos DB account with EnableGremlin capability, deployed via existing Microsoft.DocumentDB/databaseAccounts bicep in platform/fiab/bicep/modules/cosmos/ | Account already deployed for Loom's own loomdb items container |
| Traversal execution | Existing /api/items/cosmos-gremlin-graph/[id]/query → lib/azure/gremlin-client.ts over WebSocket to wss://<acct>.gremlin.cosmos.azure.com:443/ | Already 501-honest when not configured |
| Saved queries | Cosmos items container, partition cosmos-gremlin-graph | Sibling docs under same parent item |
| Vertex / edge form editor | Generated Gremlin (g.addV(label).property(...) / g.addE(label).from(g.V(id)).to(g.V(id)).property(...)) sent to the same query route | No new backend |
| Graph visualization | Client-side render via vis-network or Cytoscape.js | Both are MIT and already used in Microsoft samples |
| executionProfile() viewer | Same query route; parser of the JSON profile shape documented in execution-profile reference | Pure client-side |
| AAD auth path | ChainedTokenCredential → ARM listKeys per the Service Connector pattern; cache key in memory | Need new helper in gremlin-client.ts |
| Database-tree navigator | New BFF routes GET /api/cosmos/accounts/{name}/databases + .../databases/{db}/graphs calling Microsoft.DocumentDB/databaseAccounts/gremlinDatabases ARM list APIs | Pattern matches existing synapse-pool-arm.ts |
Required Azure resources¶
- Azure Cosmos DB account with
--capabilities EnableGremlin(already deployable via the existing Cosmos bicep module inplatform/fiab/bicep/modules/cosmos/) - AAD role assignment:
Cosmos DB Built-in Data Contributoron the account scope for the Loom UAMI (for control-plane listKeys via ARM if account-key auth is required; the data-plane WebSocket itself accepts the master key returned by listKeys) - Optional private endpoint for the Gremlin endpoint (the
.gremlin.cosmos.azure.comFQDN supports private DNS zoneprivatelink.gremlin.cosmos.azure.com) gremlinnpm package inapps/fiab-console/package.json(currently gated; runtime returns 501 if not installed)- Env vars:
LOOM_COSMOS_GREMLIN_ENDPOINT(already wired), plus a newLOOM_COSMOS_GREMLIN_AAD_ENABLED=trueto flip the credential chain
Estimated effort¶
- Session N+1 (~3 hrs) — Monaco editor with Gremlin keywords + database tree navigator (ARM list calls) + saved queries
- Session N+2 (~4 hrs) — graph visualization tab with vis-network or Cytoscape, right-click expand (
out()/in()/both()), label filter, pin/unpin - Session N+3 (~2 hrs) — table tab, Query Stats tab (RU + latency + retrieved-doc count from response headers), executionProfile() viewer
- Session N+4 (~2 hrs) — New Vertex / New Edge form editors, indexing-policy + throughput + TTL drawer, wire Edges/Vertices ribbon shortcuts, surface limits + partition-key hint
- Session N+5 (~1 hr) — AAD auth path via
ChainedTokenCredential+ ARM listKeys; Vitest + Playwright covering deferred + happy-path
Total: ~12 hrs across 5 sessions. Current grade: C. Target: A+ after session N+5 with bicep deploys the account + private endpoint + role assignments from scratch.