Skip to content

Virtual REST API

When the virtual extra is installed, HyperMesh mounts the virtual routes under /v1/virtual/* on the main API (hmdb serve). Mounted into the hypermeshdb app, the routes inherit the host app’s authentication; the optional standalone app (create_app) is unauthenticated and intended for local development only.

All request/response bodies are JSON.

MethodPathPurpose
GET/v1/virtual/backendsList supported backend types
POST/v1/virtual/proposePropose a spec from a backend’s schema
GET/v1/virtual/graphsList virtual-graph specs (summaries)
POST/v1/virtual/graphsCreate a spec
GET/v1/virtual/graphs/{id}Get the full spec
PUT/v1/virtual/graphs/{id}Replace a spec (bumps spec_version)
DELETE/v1/virtual/graphs/{id}Delete a spec
DELETE/v1/virtual/graphs/{id}/cacheClear a graph’s results cache
GET/v1/virtual/graphs/{id}/introspectBackend tables + columns
POST/v1/virtual/graphs/{id}/compileCompile HyperCypher → SQL + plan
POST/v1/virtual/graphs/{id}/queryRun HyperCypher → hydrated hyperedges
Terminal window
curl -X POST localhost:8000/v1/virtual/graphs \
-H 'content-type: application/json' \
-d '{
"id": "net",
"name": "Network Logins",
"backend": {"type": "duckdb", "path": "/data/logins.parquet"},
"nodes": [
{"label": "Machine", "table": "logins", "id_column": "x"},
{"label": "User", "table": "logins", "id_column": "x"},
{"label": "IP", "table": "logins", "id_column": "x"}
],
"edges": [{
"label": "Login",
"shape": "row",
"table": "logins",
"ts_column": "event_ts",
"row_member_columns": {"m": "src_machine", "u": "dst_user", "ip": "remote_ip"},
"members": [
{"name": "m", "node_label": "Machine", "role": "src"},
{"name": "u", "node_label": "User", "role": "dst"},
{"name": "ip", "node_label": "IP", "role": "via"}
]
}]
}'

Returns 201 with the stored spec (or 409 if the id already exists, 400 for an invalid spec).

Terminal window
curl -X POST localhost:8000/v1/virtual/graphs/net/compile \
-H 'content-type: application/json' \
-d '{"query": "MATCH (e:Login){m:Machine, u:User, ip:IP} RETURN e"}'
{
"sql": "SELECT ... FROM logins ...",
"plan": {
"edge_label": "Login",
"members": [ {"name": "m", "node_label": "Machine", "column": "src_machine", "role": "src"}, ... ],
"edge_predicates": [],
"member_predicates": [],
"size_predicates": []
}
}

Invalid or unsupported queries return 400 with the reason (e.g. a multi-pattern MATCH).

Terminal window
curl -X POST localhost:8000/v1/virtual/graphs/net/query \
-H 'content-type: application/json' \
-d '{"query": "MATCH (e:Login){m:Machine, u:User, ip:IP} WHERE m.id = '\''i-1'\'' RETURN e"}'
{
"hyperedges": [
{
"members": [
{"vertex_local_id": "machine:i-1", "role": "src"},
{"vertex_local_id": "user:alice", "role": "dst"},
{"vertex_local_id": "ip:10.0.0.1", "role": "via"}
],
"event_ts": 1700000000
}
],
"count": 1,
"sql": "SELECT ...",
"cached": false,
"cache_enabled": true
}
FieldTypeDefaultMeaning
querystringHyperCypher query text (required)
captured_atint0Provenance capture timestamp (epoch seconds)
limitint | nullnullRow cap; overrides any LIMIT in the query
refreshboolfalseBypass the results cache and recompute

Introspect a backend and get an auto-proposed spec plus a per-edge rationale. With "save": true the proposal is persisted (409 if the id exists). Set "use_llm": true with llm_api_key to refine the heuristic proposal with an OpenAI-compatible model (it falls back to the heuristic on any failure).

Terminal window
curl -X POST localhost:8000/v1/virtual/propose \
-H 'content-type: application/json' \
-d '{"backend": {"type": "duckdb", "path": "/data/logins.parquet"}, "id": "net", "name": "Network Logins"}'
StatusWhen
400invalid spec, or a compile/plan error (bad or unsupported query)
404spec id not found
409spec id already exists (create/propose with save)
502backend error (e.g. file not found, connection failed)
500hydration error while building hyperedge records