OriginChainDB
Graph

Connect records with relations

Give records named links that graph queries can follow.

A relation is a named link between records; a foreign key can check that the destination exists.

One product. One supplier.Example data · choose a record to see the rule.
shop.products
id
p001
name
Desk lamp
supplier_id
s001
shop.suppliers
id
s001
name
North Supply
No supplier with ID s999

Accepted: supplier_id points to an existing supplier.

The relation names the link; the foreign key checks that its destination exists.

The illustration uses products and suppliers; the schema below uses the same idea for orders and customers.

Declare how two tables connectA graph relation enables traversal; a foreign key separately checks whether a referenced record exists.
  1. Define both tablesChoose the primary keys and the field holding the reference.
  2. Declare the relationshipName its source field and target key.
  3. Load matching recordsUse the declared IDs when writing the linked rows.
  4. Traverse a known connectionCheck that the result points to the expected target.

Define the connection

  1. Register the destination table first, such as shop.customers.
  2. Add a column on the source table that holds the destination ID.
  3. Add a [[relations]] block naming that connection.
FieldMeaning
nameThe relationship name used in graph queries, such as placed_by.
from_colThe column on the source row that contains the destination ID.
targetDestination namespace, table and primary-key column.
bidirectionalWhen true, also creates the reverse connection.
Destination table: customers
namespace   = "shop"
table       = "customers"
primary_key = ["id"]

[[columns]]
name = "id"
ty   = "str"
required = true

[[columns]]
name = "name"
ty   = "str"

[[columns]]
name = "country"
ty   = "str"

[[columns]]
name = "referred_by"
ty   = "str"        # another customer's id

# A self-relation: customers point at customers.

[[relations]]
name          = "referrer"
from_col      = "referred_by"
target        = { namespace = "shop", table = "customers", pk = "id" }
bidirectional = true

[[indexes]]
name    = "by_id"
columns = ["id"]
Source table: orders
namespace   = "shop"
table       = "orders"
primary_key = ["id"]

[[columns]]
name = "id"
ty   = "str"
required = true

[[columns]]
name = "customer"        # <- THIS column is the edge
ty   = "str"

[[columns]]
name = "amount_cents"
ty   = "i64"

[[columns]]
name = "status"
ty   = "str"

[[columns]]
name = "notes"
ty   = "str"

[[columns]]
name = "placed_ms"
ty   = "u64"

# The declaration that turns a plain column into a traversable edge.

[[relations]]
name          = "placed_by"   # the name you pass as ?rel=
from_col      = "customer"    # the column ON THIS TABLE
target        = { namespace = "shop", table = "customers", pk = "id" }
bidirectional = true          # default; enables reverse traversal

[[indexes]]
name    = "by_id"
columns = ["id"]

A separate table containing src and dst columns does not become traversable by itself. The relation declaration is what gives a connection its graph meaning.

Saving an order maintains the declared links from its customer value. Changing that value moves the link; deleting the row removes its derived links.

Connection used in these examples

These examples use the tenant-scoped engine API and its older SDK methods. Use the connection details supplied for that interface. For the current Connect setup, see authentication. Do not combine the two URL formats.

Row-write examples
# Write an ORDINARY row. There is no edge API and nothing else to call.
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/rows/shop.orders" \
  -H "Authorization: Bearer $OC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id":           "01JTRX9KQ3YH8K2WMX0F5JZAB7",
    "customer":     "01JTRX1H4Q9P0N2WMX0F5JZ001",
    "amount_cents": 12950,
    "status":       "paid",
    "notes":        "rush delivery",
    "placed_ms":    1714478049000
  }'
# The edge order -> customer now exists. Both directions, because
# placed_by declares bidirectional = true.

If the relation's source value is a JSON array, it produces a link for each value. A missing or null value produces no link. A relation alone does not require the destination row to exist; add a foreign key for that rule.

Check the limits

  • REST graph keys: use single-column string IDs. Integer and composite keys are not addressed by these graph endpoints.
  • Cross-namespace paths: declaring a relation is separate from traversing it. Check Cypher or a supported plan for paths outside the REST endpoint's schema scope.
  • Reverse links: changing bidirectional does not backfill old rows. Rewrite affected rows when changing this setting.
  • Cypher starting points: declare the required by_id index when using an ID-based point match.

Try a traversal

Graph query guide · Prepare for Cypher · Relation fields

© 2026 Silicoyn Technologies Pvt Ltd

On this page