Relationships
Relationships
Section titled “Relationships”Including and querying related resources
Overview
Section titled “Overview”Relationships define connections between resources. Mesh supports requesting related resources in a single call, reducing round trips and enabling efficient data fetching.
Requesting Relationships
Section titled “Requesting Relationships”Use the relationships array to include related resources:
{ "call": { "function": "orders.get", "version": "1", "arguments": { "id": "12345", "relationships": ["customer", "items"] } }}relationshipsis an array of relationship names- Related resources are returned in the
includedarray - Only declared relationships can be requested
- Order in the array does not affect response
Response Structure
Section titled “Response Structure”Relationship Data
Section titled “Relationship Data”Each relationship contains a data member with resource identifiers:
{ "data": { "type": "order", "id": "12345", "attributes": { ... }, "relationships": { "customer": { "data": { "type": "customer", "id": "42" } }, "items": { "data": [ { "type": "order_item", "id": "1" }, { "type": "order_item", "id": "2" } ] } } }}Relationship Types
Section titled “Relationship Types”| Type | data Value | Description |
|---|---|---|
| To-one | { "type": "...", "id": "..." } | Single related resource |
| To-many | [{ "type": "...", "id": "..." }, ...] | Multiple related resources |
| Empty to-one | null | No related resource |
| Empty to-many | [] | No related resources |
Included Resources
Section titled “Included Resources”When relationships are requested, full resource objects appear in included:
{ "result": { "data": { "type": "order", "id": "12345", "attributes": { "status": "pending" }, "relationships": { "customer": { "data": { "type": "customer", "id": "42" } } } }, "included": [ { "type": "customer", "id": "42", "attributes": { "name": "Alice", "email": "alice@example.com" } } ] }}Compound Documents
Section titled “Compound Documents”A compound document contains the primary resource(s) plus related resources.
Structure
Section titled “Structure”{ "result": { "data": { ... }, // Primary resource(s) "included": [ ... ], // Related resources "meta": { ... } // Optional metadata }}- Each resource in
includedMUST be unique bytype+id - Resources in
includedMUST be referenced by at least one relationship includedresources MAY have their own relationships- Circular references are allowed (resource A → B → A)
Deduplication
Section titled “Deduplication”When multiple resources reference the same related resource, it appears once in included:
{ "data": [ { "type": "order", "id": "1", "relationships": { "customer": { "data": { "type": "customer", "id": "42" } } } }, { "type": "order", "id": "2", "relationships": { "customer": { "data": { "type": "customer", "id": "42" } } } } ], "included": [ { "type": "customer", "id": "42", "attributes": { "name": "Alice" } } // Customer 42 appears only once ]}Nested Relationships
Section titled “Nested Relationships”Request relationships of relationships using dot notation:
{ "relationships": ["customer", "items", "items.product"]}This includes:
customer- The order’s customeritems- The order’s line itemsitems.product- Each item’s product
Response
Section titled “Response”{ "data": { "type": "order", "id": "12345", "relationships": { "customer": { "data": { "type": "customer", "id": "42" } }, "items": { "data": [ { "type": "order_item", "id": "1" }, { "type": "order_item", "id": "2" } ] } } }, "included": [ { "type": "customer", "id": "42", "attributes": { "name": "Alice" } }, { "type": "order_item", "id": "1", "attributes": { "quantity": 2, "price": { "amount": "29.99", "currency": "USD" } }, "relationships": { "product": { "data": { "type": "product", "id": "prod_abc" } } } }, { "type": "order_item", "id": "2", "attributes": { "quantity": 1, "price": { "amount": "49.99", "currency": "USD" } }, "relationships": { "product": { "data": { "type": "product", "id": "prod_xyz" } } } }, { "type": "product", "id": "prod_abc", "attributes": { "name": "Widget", "sku": "WDG-001" } }, { "type": "product", "id": "prod_xyz", "attributes": { "name": "Gadget", "sku": "GDG-002" } } ]}Depth Limits
Section titled “Depth Limits”Servers SHOULD enforce a maximum nesting depth (e.g., 3 levels):
// Allowed"items.product.category"
// May be rejected"items.product.category.parent.parent"Allowed Relationships
Section titled “Allowed Relationships”Servers MUST define which relationships are available:
{ "relationships": ["customer", "items", "shipping_address", "billing_address"]}Validation
Section titled “Validation”- Requesting non-allowed relationships MUST return an error
- Servers SHOULD expose allowed relationships via
mesh.describe
Error Response
Section titled “Error Response”{ "errors": [{ "code": "INVALID_ARGUMENTS", "message": "Relationship not allowed: secret_notes", "retryable": false, "source": { "pointer": "/call/arguments/relationships/2" }, "details": { "relationship": "secret_notes", "allowed": ["customer", "items", "shipping_address", "billing_address"] } }]}Relationship Filtering
Section titled “Relationship Filtering”Filter the primary resource based on related resource attributes:
{ "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "pending" } ], "customer": [ { "attribute": "type", "operator": "equals", "value": "vip" } ] }, "relationships": ["customer"]}This returns orders that:
- Have status “pending”
- Have a customer with type “vip”
SQL Equivalent
Section titled “SQL Equivalent”SELECT orders.* FROM ordersJOIN customers ON customers.id = orders.customer_idWHERE orders.status = 'pending'AND customers.type = 'vip'See Filtering for complete filter syntax.
Relationship Fields
Section titled “Relationship Fields”Control which fields are returned for related resources:
{ "fields": { "self": ["id", "status", "total_amount"], "customer": ["id", "name"], "items": ["id", "quantity", "price"] }, "relationships": ["customer", "items"]}See Sparse Fieldsets for field selection details.
Without Inclusion
Section titled “Without Inclusion”Request relationship data without full resources:
// Request without relationships array{ "arguments": { "id": "12345" }}
// Response includes relationship identifiers but no included array{ "data": { "type": "order", "id": "12345", "attributes": { ... }, "relationships": { "customer": { "data": { "type": "customer", "id": "42" } } } } // No "included" - only identifiers}This allows clients to:
- See what relationships exist
- Fetch related resources separately if needed
- Reduce payload when relationships aren’t needed
Examples
Section titled “Examples”Single Relationship
Section titled “Single Relationship”// Request{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_single", "call": { "function": "orders.get", "version": "1", "arguments": { "id": "12345", "relationships": ["customer"] } }}
// Response{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_single", "result": { "data": { "type": "order", "id": "12345", "attributes": { "status": "pending", "total_amount": { "amount": "99.99", "currency": "USD" } }, "relationships": { "customer": { "data": { "type": "customer", "id": "42" } } } }, "included": [ { "type": "customer", "id": "42", "attributes": { "name": "Alice", "email": "alice@example.com" } } ] }}Multiple Relationships
Section titled “Multiple Relationships”// Request{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_multi", "call": { "function": "shipments.get", "version": "1", "arguments": { "id": "ship_123", "fields": { "self": ["id", "tracking_number", "status"], "origin": ["id", "name", "country_code"], "destination": ["id", "name", "country_code"] }, "relationships": ["origin", "destination", "events"] } }}
// Response{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_multi", "result": { "data": { "type": "shipment", "id": "ship_123", "attributes": { "tracking_number": "MH726955185FI", "status": "in_transit" }, "relationships": { "origin": { "data": { "type": "location", "id": "loc_001" } }, "destination": { "data": { "type": "location", "id": "loc_002" } }, "events": { "data": [ { "type": "tracking_event", "id": "evt_001" }, { "type": "tracking_event", "id": "evt_002" } ] } } }, "included": [ { "type": "location", "id": "loc_001", "attributes": { "name": "Helsinki Warehouse", "country_code": "FI" } }, { "type": "location", "id": "loc_002", "attributes": { "name": "Tampere Office", "country_code": "FI" } }, { "type": "tracking_event", "id": "evt_001", "attributes": { "status": "picked_up", "location": "Helsinki", "occurred_at": "2024-01-14T10:00:00Z" } }, { "type": "tracking_event", "id": "evt_002", "attributes": { "status": "in_transit", "location": "Highway", "occurred_at": "2024-01-15T08:00:00Z" } } ] }}Collection with Relationships
Section titled “Collection with Relationships”// Request{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_collection", "call": { "function": "orders.list", "version": "1", "arguments": { "fields": { "self": ["id", "status", "created_at"], "customer": ["id", "name"] }, "filters": [ { "attribute": "status", "operator": "in", "value": ["pending", "processing"] } ], "relationships": ["customer"], "pagination": { "limit": 10 } } }}
// Response{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_collection", "result": { "data": [ { "type": "order", "id": "12345", "attributes": { "status": "pending", "created_at": "2024-01-15T10:30:00Z" }, "relationships": { "customer": { "data": { "type": "customer", "id": "42" } } } }, { "type": "order", "id": "12346", "attributes": { "status": "processing", "created_at": "2024-01-15T11:00:00Z" }, "relationships": { "customer": { "data": { "type": "customer", "id": "42" } } } } ], "included": [ { "type": "customer", "id": "42", "attributes": { "name": "Alice" } } ], "meta": { "page": { "cursor": { "current": "...", "next": "..." } } } }}Nested Relationships
Section titled “Nested Relationships”// Request{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_nested", "call": { "function": "orders.get", "version": "1", "arguments": { "id": "12345", "fields": { "self": ["id", "status"], "items": ["id", "quantity"], "items.product": ["id", "name", "sku"] }, "relationships": ["items", "items.product"] } }}
// Response{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_nested", "result": { "data": { "type": "order", "id": "12345", "attributes": { "status": "pending" }, "relationships": { "items": { "data": [ { "type": "order_item", "id": "1" }, { "type": "order_item", "id": "2" } ] } } }, "included": [ { "type": "order_item", "id": "1", "attributes": { "quantity": 2 }, "relationships": { "product": { "data": { "type": "product", "id": "prod_abc" } } } }, { "type": "order_item", "id": "2", "attributes": { "quantity": 1 }, "relationships": { "product": { "data": { "type": "product", "id": "prod_xyz" } } } }, { "type": "product", "id": "prod_abc", "attributes": { "name": "Widget", "sku": "WDG-001" } }, { "type": "product", "id": "prod_xyz", "attributes": { "name": "Gadget", "sku": "GDG-002" } } ] }}Discovery
Section titled “Discovery”Functions SHOULD advertise available relationships via mesh.describe:
{ "result": { "function": "orders.list", "query": { "relationships": { "available": ["customer", "items", "shipping_address", "billing_address"], "nested": { "items": ["product", "product.category"] }, "max_depth": 3 } } }}