Skip to main content
Bring Your Own Graph (BYOG) lets you use Cypher to read and write data in HydraDB’s core graph database, which we host and scale for you in our cloud.
Migrating from an existing graph database? Jump to Migrating from Neo4j or another Cypher-compatible database.
Want to bring your own entities and relations without writing Cypher? Attach them to a document at ingest instead - see Using BYOG without Cypher.

How to use Cypher with HydraDB

Quickstart

Agents can run the same Cypher through the HydraDB MCP graph tools (hydradb_graph_query, hydradb_graph_collections, hydradb_graph_admin).

Authentication

Every request needs your HydraDB API key:
A missing or invalid key returns 403. Your databases are visible only to your organization: another organization’s databases look like names that don’t exist, and MATCH (n) RETURN n returns only your own nodes.

Endpoints

POST /byog/databases - create a database

A database groups your collections. BYOG databases are created only through this endpoint, and they are ready immediately. Creating a name that already exists returns 409.

POST /byog/query - run Cypher

  • Each collection is an isolated graph. A query runs against one collection and cannot see data in any other.
  • Collections are created automatically on the first write. Reading a collection that doesn’t exist yet returns zero rows.
  • Pass user data through params instead of building it into the query string.
  • Collection names can use letters, digits, _ and -, must start with a letter or digit, and can be up to 64 characters long.

GET /byog/collections - list collections

An unknown database returns 404.

DELETE /byog/collections - drop one collection

Drops the collection and all its data. Deleting a collection that does not exist also succeeds.

DELETE /byog/databases - drop a database

Drops the database and every collection in it.

Supported Cypher

Essentially all of openCypher is supported. Only server-side procedures and file loading are excluded. 1. Modelling and CRUD
  • Create, read, update and delete: CREATE, MATCH, MERGE, SET, REMOVE, DELETE / DETACH DELETE
  • Pattern matching, WHERE filters, aggregation, ORDER BY / SKIP / LIMIT
  • UNWIND and WITH pipelines
  • Indexes: CREATE INDEX FOR (n:Label) ON (n.prop)
  • CALL { ... } subqueries
2. Graph traversal and exploration
  • Multi-hop patterns - chain relationships in one MATCH: MATCH (a:Person)-[:KNOWS]->(b)-[:WORKS_AT]->(c:Company) RETURN c.name.
  • Variable-length traversal - follow a relationship a bounded or unbounded number of hops with *: MATCH (a:Person {name:$n})-[:KNOWS*1..4]->(reach) RETURN DISTINCT reach.name.
  • Neighborhood expansion - a node’s edges and neighbors in any direction: MATCH (p:Person {name:$n})-[r]-(nbr) RETURN type(r) AS rel, nbr.name AS neighbor.
  • Path finding - shortestPath returns the full path (nodes and edges in traversal order), not just the endpoints.
  • Directed, typed, filtered traversal - outgoing (->), incoming (<-) or either (-) edges, filtered by relationship type and by node or edge properties anywhere along the walk.
3. Not supported
These are rejected with 400 before the query runs - nothing is executed, and retrying the same query fails the same way:
  • Procedure calls - CALL some.procedure(...), including db.* and apoc.*. (CALL { ... } subqueries are fine.)
  • LOAD CSV - send data through params instead.
Dialect notes:
  • Existence checks are bare pattern predicates - MATCH (p:Person) WHERE (p)-[:KNOWS]->() RETURN p.name AS name. The EXISTS { ... } block form and the exists() function are not accepted.
  • shortestPath goes in a RETURN or WITH clause (not MATCH p = …) and the traversal must be directed.

Common Cypher queries

Load the sample graph - Five people, two companies, and the KNOWS / WORKS_AT edges between them.
Upsert a node on your own key - Updates Alice’s role, or creates her if u1 doesn’t exist.
Connect two existing nodes - Eve starts working at Acme.
Expand a node’s neighborhood - Every relationship on Bob, in any direction, and the node on the other end.
Multi-hop expansion - Everyone up to 3 hops from Alice along outgoing KNOWS edges.
Shortest path between two nodes - Returns the whole path - nodes and edges in order - not just the endpoints.
Filtered, typed traversal - Who does Alice know that works at a fintech company?
Aggregate - Headcount per company, largest first.
Index a property you filter on - Speeds up lookups by ext_id. A write with no RETURN returns an empty list.
Delete a node and its edges - Removes Eve and every relationship attached to her.

Response format

Rows come back in data - one object per result row, keyed by your RETURN aliases:
  • Alias every column you read (RETURN n.name AS name). An unaliased column is keyed by its expression text ("n.name").
  • A write with no RETURN succeeds with data: [].
What each kind of value looks like inside a row:
  • Nodes and relationships are flat objects: their properties plus the keys shown above. A stored property with one of those names (id, labels, relation, source_node_id, target_node_id) is hidden in the response - alias it (RETURN n.id AS my_id).
  • id is internal: it can be reused after deletes and does not survive an export/re-import. Key your data on a property you own (ext_id, email, …).
  • Integers are 64-bit. Values beyond 2⁵³ lose precision in languages that parse JSON numbers as doubles - return them as strings.

Using results in your code

A minimal client

Wrap the endpoint once. Success puts rows in data; errors are wrapped in detail.

Reading rows

Pagination

Result sets past the deployment cap are silently truncated, so page any read that could be large. A stable ORDER BY keeps pages consistent:

Bulk loading

Chunk rows to stay inside the 256 KiB body cap and the 30 s write budget; MERGE on your own key makes the load re-runnable after a failure:

Handling failures

  • 400 - the message tells you what to fix: your Cypher (compiler feedback is passed through) or a query that needs LIMIT/an index (budget timeout). Retrying unchanged will fail identically.
  • 429 / 500 - transient; retry with backoff. Writes built on MERGE (as above) are safe to retry; bare CREATE batches are not idempotent, so a retried chunk can duplicate nodes.

Errors and limits

Errors come back with success: false and the reason in error (also mirrored in detail):
A query counts as a write (and gets the larger budget) when it contains any write clause - CREATE, MERGE, SET, DELETE, REMOVE, FOREACH.
  • Paginate anything potentially large: ORDER BY … SKIP $offset LIMIT $page. Without an ORDER BY, rows dropped at the result-set cap are arbitrary.
  • Chunk bulk imports into UNWIND $rows batches sized to finish inside the 30 s write budget (and the 256 KiB body cap).
  • Create indexes for properties you filter on - CREATE INDEX FOR (n:Person) ON (n.name). A slow read usually needs one.

Migrating from Neo4j or another Cypher-compatible database

Most application Cypher from Neo4j, Memgraph, or any other Cypher-compatible database ports directly. The differences you are most likely to notice:
  • Procedure calls (for example Neo4j’s CALL db.* / CALL apoc.*) are not available - the equivalents are either plain Cypher or not part of the supported surface.
  • LOAD CSV is not available - batch data in through params.
  • Internal node ids are not portable - migrate using your own key properties, for example UNWIND $rows AS row MERGE (n:Person {ext_id: row.ext_id}) SET n += row.
Migrate one collection at a time: export a graph, replay it into one collection, and verify the counts. The script below reads from Neo4j with the official driver and writes to HydraDB with the HydraGraph client. Any source you can read nodes and relationships from works the same way. It loads nodes first, then relationships matched on your own key. Cypher doesn’t allow a relationship type as a parameter, so it writes one batch per type:

Using BYOG without Cypher

To use your own entities and relations for a document or memory instead of HydraDB’s LLM extraction, pass a graph_payload on POST /context/ingest. It is a JSON map keyed by source id (a document_metadata id, an app_knowledge id, or a memory id):
  • Replaces extraction for each keyed source; the source is still chunked and embedded. Relations surface in /query graph_context (tagged origin: "byog") - no query-side changes.
  • Persists across re-ingest - re-ingesting without a graph_payload re-applies the stored graph; sending a new one replaces it.
  • Limits - ≤ 5,000 entities, ≤ 10,000 relations, ≤ 500 relations per entity; oversized payloads return 400. A key matching no source in the request is rejected with 400.
  • Whole-graph only: no per-triple updates, and relations link to their best-matching chunk even when the match is weak.
See the graph_payload field reference for the full shape.