This document (version 0.3) defines a graph module according to the terms set out in the core specification.
The graph module describes how ontology types and entities can be created, queried, updated, and linked together – including the block entity (the entity associated directly with the block).
Entity: an instance of data, often corresponding to a thing in the world, constrained by a particular entity type.
Link Entity: a kind of entity which represents a connection or relation between its left and right entity, constrained by a link entity type.
Left Entity: the entity which is on the "left" of a specific link entity – in most situations this can be thought of as the "source" of the link.
Right Entity: the entity which is on the "right" of a specific link entity – in most situations this can be thought of as the "target" of the link.
Ontology Type: an element of the Block Protocol Type System, represented as a self-contained schema accessible via a URL. Ontology types are composed together to describe the shape of entities within the graph and the potential links between them.
Entity type: an ontology type that defines the structure of an entity, the type of link entities it may be connected to and the type of entities on the other end of those links. An entity type references other entity types and property types.
Link Entity type: an entity type which has a special marker to say it defines the structure of a link entity.
Property type: an ontology type that defines the structure of an individual property within an entity. A property type references other property types and data types.
Data type: an ontology type that defines the possible space of possible values that an individual property can take within an entity.
Graph: a network of graph elements (entities, entity types, property types, and data types) connected by edges.
Edge: a directed connection between two graph elements which describes their relationship. Possible edges include those that describe references between types, or the relationship between the left and right entities of link entities.
Subgraph: a specific subset of a graph, usually returned as a result of a query. Can contain a set of roots which are distinguished elements of the subgraph, and usually correspond to the direct results of the query – the subgraph may contain other elements of the graph that were connected to the root elements through various edges.
Block entity: an entity associated with the specific instance of the block, conforming to block's specified entity type.
Block Entity Subgraph: a subgraph which contains the block entity as its root, with connected elements to a given depth.
The data model of the graph module is defined through its Type System. The type system describes how a graph of entities is structured, through a composition of the 3 classes of ontology types: data types, property types, and entity types.
When composed, these types are capable of describing any JSON object.
A data type describes a space of possible valid values.
These cover all possible primitive values that can be represented within JSON, as well as Object
s and Empty List
s which are included for completeness of the system (see the RFC for rationale).
At present, there are 6 primitive data types. New ones cannot be created, although that is a planned extension in the near-future, whereby new ones will be defined by combining the primitive data types and constraints such as max lengths, minimum values, etc. (see the associated in-development RFC).
When represented as JSON schema, the 6 primitive data types are:
{
"$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/data-type",
"kind": "dataType",
"$id": "https://blockprotocol.org/@blockprotocol/types/data-type/text/v/1",
"title": "Text",
"description": "An ordered sequence of characters",
"type": "string"
}
{
"$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/data-type",
"kind": "dataType",
"$id": "https://blockprotocol.org/@blockprotocol/types/data-type/number/v/1",
"title": "Number",
"description": "An arithmetical value (in the Real number system)",
"type": "number"
}
{
"$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/data-type",
"kind": "dataType",
"$id": "https://blockprotocol.org/@blockprotocol/types/data-type/boolean/v/1",
"title": "Boolean",
"description": "A True or False value",
"type": "boolean"
}
{
"$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/data-type",
"kind": "dataType",
"$id": "https://blockprotocol.org/@blockprotocol/types/data-type/null/v/1",
"title": "Null",
"description": "A placeholder value representing 'nothing'",
"type": "null"
}
{
"$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/data-type",
"kind": "dataType",
"$id": "https://blockprotocol.org/@blockprotocol/types/data-type/object/v/1",
"title": "Object",
"description": "A plain JSON object with no pre-defined structure",
"type": "object"
}
{
"$schema": "https://blockprotocol.org/types/modules/graph/0.3/schema/data-type",
"kind": "dataType",
"$id": "https://blockprotocol.org/@blockprotocol/types/data-type/empty-list/v/1",
"title": "Empty List",
"description": "An Empty List",
"type": "array",
"const": []
}
A property type is the definition of the possible values that can be associated with a property. When represented in JSON schema, a property type satisfies the following meta-schema:
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://blockprotocol.org/types/modules/graph/0.3/schema/property-type",
"title": "Property Type",
"description": "Specifies the structure of a Block Protocol property type",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"const": "https://blockprotocol.org/types/modules/graph/0.3/schema/property-type"
},
"kind": {
"const": "propertyType"
},
"$id": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/versioned-url"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"oneOf": {
"type": "array",
"items": {
"$ref": "#/$defs/propertyValues"
}
}
},
"required": [
"$schema",
"kind",
"$id",
"title",
"description",
"oneOf"
],
"additionalProperties": false,
"$defs": {
"propertyValues": {
"title": "propertyValues",
"description": "The definition of potential property values, either references to data types, objects made up of more property types, or an array where the items are defined from a set of other property values definitions.",
"oneOf": [
{
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/data-type-reference"
},
{
"title": "propertyObjectValue",
"type": "object",
"properties": {
"type": {
"const": "object"
},
"properties": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/property-type-object"
}
},
"minProperties": 1,
"required": [
"type",
"properties"
]
},
{
"title": "propertyArrayValue",
"type": "object",
"properties": {
"type": {
"const": "array"
},
"items": {
"type": "object",
"properties": {
"oneOf": {
"type": "array",
"items": {
"$ref": "#/$defs/propertyValues"
},
"minItems": 1
}
},
"required": [
"oneOf"
],
"additionalProperties": false
},
"minItems": {
"type": "integer",
"minimum": 0
},
"maxItems": {
"type": "integer",
"minimum": 0
}
},
"required": [
"type",
"items"
],
"additionalProperties": false
}
]
}
}
}
An entity type is the definition of (1) the structure of an entity's properties, and (2) its outgoing link entities and their endpoint entities. When represented in JSON schema, an entity type satisfies the following meta-schema:
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type",
"title": "Entity Type",
"description": "Specifies the structure of a Block Protocol entity type",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"const": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type"
},
"kind": {
"const": "entityType"
},
"$id": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/versioned-url"
},
"type": {
"const": "object"
},
"title": {
"type": "string"
},
"titlePlural": {
"type": "string"
},
"inverse": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"titlePlural": {
"type": "string"
}
},
"additionalProperties": false
},
"description": {
"type": "string"
},
"allOf": {
"$comment": "Present if this entity type is a parent entity type.",
"type": "array",
"items": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type-reference"
},
"minItems": 1
},
"properties": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/property-type-object"
},
"required": {
"type": "array",
"items": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/base-url"
}
},
"links": {
"type": "object",
"propertyNames": {
"$comment": "Property names must be a valid versioned URL to a link entity type to allow for outgoing links of that type from this entity.",
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/versioned-url"
},
"patternProperties": {
".*": {
"type": "object",
"properties": {
"type": {
"const": "array"
},
"items": {
"$ref": "#/$defs/oneOfEntityTypeReference"
},
"minItems": {
"type": "integer",
"minimum": 0
},
"maxItems": {
"type": "integer",
"minimum": 0
}
},
"required": [
"type",
"items"
],
"additionalProperties": false
}
}
},
"labelProperty": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/base-url"
},
"icon": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"$schema",
"kind",
"type",
"$id",
"title",
"description",
"properties"
],
"$defs": {
"oneOfEntityTypeReference": {
"description": "Specifies a set of entity types inside a oneOf",
"type": "object",
"properties": {
"oneOf": {
"type": "array",
"items": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type-reference"
}
}
},
"additionalProperties": false
}
}
}
Blocks using the graph module provide an entity type which describes the shape of their block entity. Embedding applications should use this to constrain the entity they send to blocks when they are instantiated, and the entities which are linked to it as part of the block subgraph.
The versioned URL of the entity type's schema must be referenced in the block's block-metadata.json
under a schema
key.
Blocks may also include an example-graph
file (either example-graph.ts
or example-graph.json
depending on approach) which defines example data that a block can use.
A block using the graph module MAY expand block-metadata.json
to specify:
schema
: the versioned URL of the entity type associated with the block entity. As per the specification, the schema hosted at the remote address should not be subject to change and versions of entity types should be treated as immutable, permanently available, records.variants
: an array of objects, each with a name
and properties
, and optionally description
, icon
, and examples
. These objects represents different variants of the block that the user can create, where properties
sets the starting properties. As a simple example, a ‘header’ block might have variants with the name
‘Heading 1’ and ‘Heading 2’, which start with { "https://blockprotocol.org/@alice/types/property-type/level/": 1 }
and { "https://blockprotocol.org/@alice/types/property-type/level/": 2 }
as properties
, respectivelyA block using the graph module MAY include an example-graph.json
file alongside its metadata. The file's contents
entities
key where each element is a JSON representation of an entity which MUST conform to the structure specified in entity definition.blockEntityRecordId
key, which is the EntityRecordId
of the associated block entity in the entities
arrayAn embedding application MUST use the constraints set in the associated entity type to constrain the data it sends to the block in the block entity subgraph.
An entity is a combination of metadata and properties.
The metadata is a minimum collection of fields that are fixed and specified in this document, although embedding applications MAY choose to add their own.
The properties is a JSON object which satisfies the constraints of the entity’s entity type.
When embedding applications pass entities to blocks, whether the block entity or any other:
entities MUST be represented by an object, which:
MUST contain a field metadata
, an object which:
MUST contain a field recordId
, an object which:
entityId
uniquely identifying the entity in the application, which MUST be a string.editionId
which identifies the specific edition of the entity, which MUST be a string, and MUST be unique for each edition of an entity for applications that have versioning for their data.MUST contain an entityTypeId
identifying the entity type the entity belongs to, which MUST be a versioned URL from which a valid entity type schema is retrievable.
MUST contain a field properties
if the block has defined any required
properties in its schema, and otherwise MAY be omitted. If present, it:
if the entity is a link entity it MUST (and if it is not, MUST NOT) contain a field linkData
, an object which:
leftEntityId
identifying the left entity of the link, which MUST be a stringrightEntityId
identifying the right entity of the link, which MUST be a stringleftToRightOrder
used to order this link entity against other link entities of the same type with the same leftEntityId
, which MUST be a non-negative integerrightToLeftOrder
used to order this link entity against other link entities of the same type with the same rightEntityId
, which MUST be a non-negative integeri.e. any entity MUST conform to the following schema:
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity",
"title": "Entity",
"type": "object",
"properties": {
"properties": {
"type": "object",
"$comment": "This should be constrained by the associated entity type",
"propertyNames": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/base-url"
}
},
"metadata": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/entity-metadata"
},
"linkData": {
"$ref": "https://blockprotocol.org/types/modules/graph/0.3/schema/link-data"
}
},
"required": [
"metadata"
]
}
Blocks must pass an entityId
when making requests in respect of any entity.
Embedding applications should ensure that the entityId
is stable and unique for any given entity, across any versions of it that may exist.
Blocks MUST provide entityId
when identifying a particular entity for the purpose of messages under the graph module.
Embedding applications MUST provide entityId
for each entity it supplies the block at the root of the entity data object, as described under Structure above.
entityId
SHOULD be a stable, unique reference to the entity, so that blocks may use it for comparing the identity of entities – i.e. so that blocks can assume that entities with the same entityId
are the same entity, and that the same entity referenced in different places will have the same entityId
wherever it appears.
Many of the values returned by embedding applications for messages of the graph module include a subgraph.
A subgraph is a data structure which contains elements of the graph, vertices, and edges which is a precomputed lookup set of the relationships between elements of the graph.
A subgraph is a JSON object with the following fields, each described in detail below:
roots
vertices
edges
depths
A Vertex
is a JSON object which satisfies the following schema:
{
"type": "object",
"properties": {
"kind": {
"oneOf": ["entity"]
},
"inner": {
"$comment": "the entity",
"type": "object"
}
},
"required": ["kind", "inner"]
}
Note: The
kind
field is used to allow for future expansion of the subgraph format to include other kinds of vertices, such as ontology types.
Vertices
objectThe vertices
field of a subgraph is a JSON object where:
entityId
srevisionId
s - a unique identifier for an instance of a specific entity within a subgraph, and MUST be a string. This is a forward-facing field which currently doesn't serve a functional purpose and as such can be any string.Vertex
objectsNote: The
revisionId
will be used to identify different revisions of the same element within a single subgraph. This could be multiple versions of the same ontology type, or multiple revisions of an entity in an application that supports versioning.
The roots of a subgraph are distinguished elements which were the starting points for traversal of edges (up to depths defined by the graph resolve depths).
The roots
field of the subgraph is an array of Vertex IDs which identify the roots, and are JSON objects which satisfy the following schema:
{
"type": "object",
"properties": {
"baseId": {
"type": "string"
},
"revisionId": {
"type": "string"
}
},
"required": ["baseId", "revisionId"]
}
The baseId
field MUST correspond to the key of the vertices
object which is the parent of the object that contains the vertex of the root.
The revisionId
field MUST correspond to the key of the object of vertices[baseId]
which contains the vertex of the root.
That is to say, subgraph.vertices[baseId][revisionId]
MUST be the vertex of the root in question.
An outward edge is a partial definition of an edge between two vertices, which defines the edge-variant and one of the endpoints of the edge.
When used in the context of the Edges
object, it fully qualifies the definition of a leftEndpoint - edge -> rightEndpoint
It's made up of a trio of information:
reversed
kind
the kind of edge, which is one of:
HAS_LEFT_ENTITY
HAS_RIGHT_ENTITY
rightEndpoint
the entityId
of the right endpoint of the edgeThe fields lead to the following permutations:
Entity has outgoing link (the entity on the left endpoint has an outgoing link entity)
{
"reversed": true,
"kind": "HAS_LEFT_ENTITY",
"rightEndpoint": "Some Entity Id"
}
Entity has incoming link (the entity on the left endpoint has an incoming link entity)
{
"reversed": true,
"kind": "HAS_RIGHT_ENTITY",
"rightEndpoint": "Some Entity Id"
}
Link Entity has left entity (the link entity on the left endpoint has a left entity)
{
"reversed": false,
"kind": "HAS_LEFT_ENTITY",
"rightEndpoint": "Some Entity Id"
}
Link Entity has right entity (the link entity on the left endpoint has a right entity)
{
"reversed": false,
"kind": "HAS_RIGHT_ENTITY",
"rightEndpoint": "Some Entity Id"
}
Specifically an OutwardEdge
is a JSON object which satisfies the following schema:
{
"type": "object",
"properties": {
"kind": {
"oneOf": ["HAS_LEFT_ENTITY", "HAS_RIGHT_ENTITY"]
},
"reversed": {
"type": "boolean"
},
"rightEndpoint": {
"$comment": "The entity ID of the right endpoint of this edge",
"type": "string"
}
},
"required": ["kind", "reversed", "rightEndpoint"]
}
Edges
objectThe edges
field of a subgraph is a JSON object where:
entityId
sOutwardEdge
objects of the edges that have an endpoint at the entity associated with the entityId
.A subgraph is constructed by exploring the datastore along edges from a set of elements. This traversal is limited by a set of resolve depths which constrain the depth of the query.
The resolveDepths
field of the subgraph is a JSON object which:
hasLeftEntity
hasRightEntity
hasLeftEntity
and hasRightEntity
MUST be a JSON object which:
incoming
and the value MUST be an integer between 0 and 255, which defines the number of incoming edges to explore of that edge kindoutgoing
and the value MUST be an integer between 0 and 255, which defines the number of outgoing edges to explore of that edge kindWhile traversing, the depths are decremented according to the following pseudo-code:
export const traverseElement = ({
datastore,
element,
currentTraversalDepths,
}) => {
// handle element
for (const [edgeKind, depthsPerDirection] of currentTraversalDepths) {
for (const [direction, depth] of depthsPerDirection) {
if (depth < 1) {
continue;
}
for (const neighborVertex of getNeighbors(element, edgeKind, direction)) {
traverseElement({
datastore,
neighborVertex,
currentTraversalDepths: {
...currentTraversalDepths,
[edgeKind]: {
...currentTraversalDepths[edgeKind],
[direction]: depth - 1,
},
},
});
}
}
}
};
The following error codes MAY appear as a value for code
in an entry in the errors
array sent with any message:
NOT_FOUND
: the message relied on an entity that was not foundFORBIDDEN
: the message relied on permissions that the user or block does not haveINVALID_INPUT
: the message relied on previous input that was missing or otherwise invalidNOT_IMPLEMENTED
: the embedding application or block does not support the requested operationINTERNAL_ERROR
: the embedding application encountered an internal error while executing the requestIndividual messages MAY specify only a subset of these codes as being valid for that message.
Messages under the graph module can be grouped into two functional categories:
source: "embedder"
and sentOnInitialization: true
source: "block"
Note that there is no reason why embedding applications cannot make requests, nor why blocks cannot send values on initialization, but the module does yet not specify any such messages.
The values are all sent from the embedding application to the block on initialization, and re-sent subsequently if they change. They are:
blockEntitySubgraph
: the subgraph rooted at the block entity, resolved to a depth determined by the embedding application and reported in the depths
field of the subgraphreadonly
: whether or not the block should display in ‘readonly’ mode, disabling or hiding any editing controls it offers.The requests may be made by the block once it has been initialized, and represent operations which create, read, update or delete entities. They are named according to the pattern [action][Resource]
, e.g. createEntity
.
As well as the formal module JSON specification, the messages can be sent and received via the @blockprotocol/graph
package.
The messages available for exchange in the graph module are defined in the module's JSON definition and are also listed below for ease of reference.
createEntity[block]
createEntityResponse[embedder]
updateEntity[block]
updateEntityResponse[embedder]
deleteEntity[block]
deleteEntityResponse[embedder]
getEntity[block]
getEntityResponse[embedder]
queryEntities[block]
queryEntitiesResponse[embedder]
uploadFile[block]
uploadFileResponse[embedder]
createEntity
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityTypeId | versioned-url | yes | The entityTypeId of the type of the entity to create |
properties | property-type-object | yes | The properties of the entity to create |
linkData | link-data | no | Link data if the entity is a link entity |
errorCodes
: nonerespondedToBy
: createEntityResponsesentOnInitialization
: false
createEntityResponse
source
: embedderdata
: entity
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_IMPLEMENTED
, INTERNAL_ERROR
respondedToBy
: nonesentOnInitialization
: false
updateEntity
source
: blockdata
: object
property | type | required | description |
---|
errorCodes
: nonerespondedToBy
: updateEntityResponsesentOnInitialization
: false
updateEntityResponse
source
: embedderdata
: entity
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
, NOT_IMPLEMENTED
, INTERNAL_ERROR
respondedToBy
: nonesentOnInitialization
: false
deleteEntity
source
: blockdata
: entity-id
errorCodes
: nonerespondedToBy
: deleteEntityResponsesentOnInitialization
: false
deleteEntityResponse
source
: embedderdata
: boolean
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
, NOT_IMPLEMENTED
, INTERNAL_ERROR
respondedToBy
: nonesentOnInitialization
: false
getEntity
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityId | entity-id | yes | The entityId of the entity to retrieve |
graphResolveDepths | partial-graph-resolve-depths | no |
errorCodes
: nonerespondedToBy
: getEntityResponsesentOnInitialization
: false
getEntityResponse
source
: embedderdata
: subgraph
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
, NOT_IMPLEMENTED
, INTERNAL_ERROR
respondedToBy
: nonesentOnInitialization
: false
queryEntities
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
operation | query-operation | yes | |
graphResolveDepths | partial-graph-resolve-depths | no |
errorCodes
: nonerespondedToBy
: queryEntitiesResponsesentOnInitialization
: false
queryEntitiesResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_IMPLEMENTED
, INTERNAL_ERROR
respondedToBy
: nonesentOnInitialization
: false
uploadFile
source
: blockdata
:
errorCodes
: nonerespondedToBy
: uploadFileResponsesentOnInitialization
: false
uploadFileResponse
source
: embedderdata
: remote-file
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
, NOT_IMPLEMENTED
, INTERNAL_ERROR
respondedToBy
: nonesentOnInitialization
: false
Previous
Next
Anyone with an existing application who wants to embed semantically-rich, reusable blocks in their product can use the protocol. Improve your app’s utility and tap into a world of structured data with no extra effort, for free.
Any developer can build and publish blocks to the Hub for others to use. Contribute to an open-source community building a more interoperable future web.