Specification scope: Core with the Relations companion specification.
A property named customerId suggests a relation but does not declare its
target or resolution rules.
The string might identify a customer, an account, or a CRM import row. It might be unique in this document, unique in a database, or not unique at all. The name suggests a foreign key while leaving its target and resolution rules in prose.
The relations extension gives the schema vocabulary for those rules:
identity, relations, targettype, cardinality, and scope.
Identity starts at the target
An identity belongs to an object or tuple type. Its value is an ordered array of property names. One property gives a simple identity; several give a composite identity.
The complete example below declares Customer.id as the customer identity and
Order.id as the order identity. The customer relation on Order targets
Customer and resolves within the document’s customers collection.
{
"$schema": "https://json-structure.org/meta/extended/v0/#",
"$id": "https://example.com/schemas/commerce-document",
"name": "CommerceDocumentSchema",
"$root": "#/definitions/CommerceDocument",
"definitions": {
"Customer": {
"name": "Customer",
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" }
},
"required": ["id", "name"],
"additionalProperties": false,
"identity": ["id"]
},
"Order": {
"name": "Order",
"type": "object",
"properties": {
"id": { "type": "string" },
"total": { "type": "decimal" }
},
"relations": {
"customer": {
"targettype": { "$ref": "#/definitions/Customer" },
"cardinality": "single",
"scope": "#/definitions/CommerceDocument/properties/customers"
}
},
"required": ["id", "total"],
"additionalProperties": false,
"identity": ["id"]
},
"CommerceDocument": {
"name": "CommerceDocument",
"type": "object",
"properties": {
"customers": {
"type": "array",
"items": { "type": { "$ref": "#/definitions/Customer" } }
},
"orders": {
"type": "array",
"items": { "type": { "$ref": "#/definitions/Order" } }
}
},
"required": ["customers", "orders"],
"additionalProperties": false
}
}
}
A corresponding document contains the relation as an ordinary JSON property:
{
"$schema": "https://example.com/schemas/commerce-document",
"customers": [
{ "id": "C-1042", "name": "Ada Lovelace" }
],
"orders": [
{
"id": "O-9001",
"total": "149.50",
"customer": { "identity": "C-1042" }
}
]
}
The familiar conceptual link Order.customerId -> Customer.id is present, but
it is not modeled as a bare customerId property. The draft gives relation
instances their own shape. A single relation is an object whose identity
member matches the target type’s identity.
Target and cardinality are explicit
Every relation declaration requires targettype and cardinality.
targettype is a schema containing a $ref to a type with an identity
declaration. That rule prevents a relation from pointing vaguely at an object
shape that has no declared matching key.
cardinality is either single or multiple. single means exactly one
target instance and is represented by one relation object. multiple means zero
or more targets and is represented by an array of relation objects.
Cardinality is not inferred from an English plural or a property name. The schema says it.
Scope says where to look
scope is a JSON Pointer, or an array of JSON Pointers, to schema locations for
collections in the same document. A target collection must be an array,
set, or map compatible with targettype. Map resolution searches values,
not keys.
In the example, the resolver follows
#/definitions/CommerceDocument/properties/customers, then finds the customer
whose declared identity equals C-1042.
Omit scope and the meaning changes: the target exists outside the document.
The application must resolve it through an external database, service, or other
source. The absence of scope is therefore not shorthand for “search
everywhere in this JSON document.”
Composite identities preserve order
If a target declares "identity": ["isbn", "edition"], a relation instance
uses an array in that same order:
{ "identity": ["978-0-123456-78-9", 2] }
That order is part of the contract. It is the relation equivalent of a composite primary key.
Resolution remains a processor task
identity and relations make reference intent machine-readable, but they do
not prescribe an external query protocol. A core-only validator need not
enforce uniqueness across a collection or dereference an external service. A
relations-aware processor must perform those checks.