Quickstart
Create a database, write two nodes, and read them back:hydradb_graph_query, hydradb_graph_collections, hydradb_graph_admin).
Authentication
Every request needs your HydraDB API key:403. Your databases are visible only to
your organization: another organization’s databases look like names that
do not exist, and MATCH (n) RETURN n returns only your own nodes.
Endpoints
POST /byog/databases: create a database
A database groups your collections. Creating a database here makes it ready
immediately; a database created with the standard POST /databases can also
hold graph collections. 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 does not exist yet returns zero rows.
- Pass user data through
paramsinstead 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 graph collections and, if created through POST /byog/databases, the database itself. For a database created through the standard database API, the graph collections are dropped, deleted is false, and the database stays. Use the standard DELETE /databases to remove that database.
Supported Cypher
All of openCypher is supported except server-side procedures and file loading.Modeling and CRUD
- Create, read, update and delete:
CREATE,MATCH,MERGE,SET,REMOVE,DELETE/DETACH DELETE - Pattern matching,
WHEREfilters, aggregation,ORDER BY/SKIP/LIMIT UNWINDandWITHpipelines- Indexes:
CREATE INDEX FOR (n:Label) ON (n.prop) CALL { ... }subqueries
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:
shortestPathreturns the full path (nodes and edges in traversal order), not only 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.
Not supported
These are rejected with400 before the query runs; nothing is executed,
and retrying the same query fails the same way:
- Procedure calls:
CALL some.procedure(...), includingdb.*andapoc.*.CALL { ... }subqueries are supported. LOAD CSV: send data throughparamsinstead.
Dialect notes
- Existence checks are bare pattern predicates:
MATCH (p:Person) WHERE (p)-[:KNOWS]->() RETURN p.name AS name. TheEXISTS { ... }block form and theexists()function are not accepted. shortestPathgoes in aRETURNorWITHclause (notMATCH p = …) and the traversal must be directed.
Common Cypher queries
Each query runs against the sample graph that the first one loads.Load the sample graph
Five people, two companies, and theKNOWS / WORKS_AT edges between them.
Result
Result
Upsert a node on your own key
Updates Alice’s role, or creates her ifu1 does not exist.
Result
Result
Connect two existing nodes
Eve starts working at Acme.Result
Result
Expand a node’s neighborhood
Every relationship on Bob, in any direction, and the node on the other end.Result
Result
Multi-hop expansion
Everyone up to 3 hops from Alice along outgoingKNOWS edges.
Result
Result
Shortest path between two nodes
Returns the whole path (nodes and edges in order), not only the endpoints.Result
Result
Filtered, typed traversal
Who does Alice know that works at a fintech company?Result
Result
Aggregate
Headcount per company, largest first.Result
Result
Index a property you filter on
Speeds up lookups byext_id.
Result
Result
Delete a node and its edges
Removes Eve and every relationship attached to her.Result
Result
Response format
Rows come back indata: 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
RETURNsucceeds withdata: [].
- Nodes and relationships are flat objects: their properties plus the keys
in the table. 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). idis internal: it can be reused after deletes and does not survive an export and re-import. Key your data on a property you own, such asext_idoremail.- Integers are 64-bit. Values beyond 2⁵³ lose precision in languages that parse JSON numbers as doubles; return them as strings.
Use results in your code
A minimal client
Wrap the endpoint once. A success puts rows indata; the client reads an
error from detail.
Read rows
Pagination
Result sets past the deployment cap are silently truncated, so page any read that could be large. A stableORDER 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:
Errors and limits
Errors
Errors come back withsuccess: false and the reason in error (also
mirrored in detail):
Decide whether to retry by status:
400: do not retry unchanged; it fails identically. The message says what to fix, either your Cypher or a query that needsLIMITor an index (budget timeout).429and500: transient; retry with backoff. Writes built onMERGE, as in Bulk loading, are safe to retry. BareCREATEbatches are not idempotent, so a retried chunk can duplicate nodes.
Limits and timeouts
A query counts as a write (and gets the larger budget) when it contains
any write clause:
CREATE, MERGE, SET, DELETE, REMOVE, FOREACH.
To stay inside these limits:
- Paginate anything potentially large: see Pagination.
Without an
ORDER BY, rows dropped at the result-set cap are arbitrary. - Chunk bulk imports: see Bulk loading.
- Create indexes for properties you filter on:
CREATE INDEX FOR (n:Person) ON (n.name). A slow read usually needs one.
Migrate 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.*andCALL apoc.*) andLOAD CSVare not supported. The equivalents of a procedure are either plain Cypher or not part of the supported surface. Instead ofLOAD CSV, batch data in throughparams. - Existence checks and
shortestPathtake a different form: see Dialect notes. - 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.
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 does not allow a
relationship type as a parameter, so it writes one batch per type. It copies
Person nodes and KNOWS relationships; repeat both steps for every other
label and relationship type in your graph, and compare each count:
Use BYOG without Cypher
To use your own entities and relations for a source instead of the ones HydraDB extracts, pass agraph_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: HydraDB skips extraction for each keyed source;
the source is still chunked and embedded. Relations appear in
/querygraph_contexttaggedorigin: "byog", with no query-side changes. - Persists across re-ingest: re-ingesting without a
graph_payloadre-applies the stored graph; sending a new one replaces it. - Limits: at most 5,000 entities, 10,000 relations, and 500 relations per
entity; oversized payloads return
400. A key matching no source in the request is rejected with400. - Whole-graph only: there are no per-triple updates, and relations link to their best-matching chunk even when the match is weak.
graph_payload field reference
for the full shape.