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.
Endpoints
Section titled “Endpoints”| Method | Path | Purpose |
|---|---|---|
GET | /v1/virtual/backends | List supported backend types |
POST | /v1/virtual/propose | Propose a spec from a backend’s schema |
GET | /v1/virtual/graphs | List virtual-graph specs (summaries) |
POST | /v1/virtual/graphs | Create 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}/cache | Clear a graph’s results cache |
GET | /v1/virtual/graphs/{id}/introspect | Backend tables + columns |
POST | /v1/virtual/graphs/{id}/compile | Compile HyperCypher → SQL + plan |
POST | /v1/virtual/graphs/{id}/query | Run HyperCypher → hydrated hyperedges |
Create a spec
Section titled “Create a spec”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).
Compile a query
Section titled “Compile a query”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).
Run a query
Section titled “Run a query”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}Query body fields
Section titled “Query body fields”| Field | Type | Default | Meaning |
|---|---|---|---|
query | string | — | HyperCypher query text (required) |
captured_at | int | 0 | Provenance capture timestamp (epoch seconds) |
limit | int | null | null | Row cap; overrides any LIMIT in the query |
refresh | bool | false | Bypass the results cache and recompute |
Propose a spec
Section titled “Propose a spec”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).
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"}'Error codes
Section titled “Error codes”| Status | When |
|---|---|
400 | invalid spec, or a compile/plan error (bad or unsupported query) |
404 | spec id not found |
409 | spec id already exists (create/propose with save) |
502 | backend error (e.g. file not found, connection failed) |
500 | hydration error while building hyperedge records |