This document (version 0.2) defines a graph service according to the terms set out in the core specification.
The graph service describes how entities can be created, queried, updated, and linked together – including the block entity, represented by the properties sent to the block.
Block entity: an instance of data associated with the block, conforming to the block’s schema.
Entity: an instance of data, often corresponding to a thing in the world, of a particular entity type.
Entity type: a specific type of entity, with a schema defining its structure. Entities have a type, and types have a schema.
Graph: a network of entities connected by links.
Link: a connection or relation between two entities.
Linked entities: entities which are linked to the block entity, whether directly or via other entities.
Properties: the fields on a schema, what types of data are permitted as their values, and any other validation rules. Field types may be scalar (integer, string etc.), complex objects, collections (lists/arrays), or refer to another schema.
Schema: a definition of a data structure, associated with an entity type. Entities of a given type must have properties which conform to the type’s schema.
Blocks using the graph service may provide a schema which describes the shape of their own properties, if any. Embedding applications should use this to constrain the properties they send to blocks. These properties are part of the block entity.
The block’s schema may be included as a file in the block package, or referred to by remote address (a URL). If the block has a schema, a path to it must be added to their block-metadata.json
, under a schema
key.
Blocks may also add default
and examples
keys to block-metadata.json
to define default and example properties conforming to their schema. If more complex example data is required that involves specifying other entities and links between them, which the block will make use of, a block package may also include an example-graph.json
.
A block using the graph service MAY expand block-metadata.json
to specify:
schema
: a path to a JSON file describing the properties the block accepts as part of the block entity, using JSON Schema vocabulary. This file may be included in the block package, or hosted at a remote address. If the schema hosted at a remote address is subject to change, bear in mind that changes in the schema may invalidate other values in the block metadata (e.g. default
) or the block’s functioning (e.g. if important constraints are removed).default
: an object, conforming to the block’s schema, representing the default data that applications should provide when creating a block, unless (a) the block can handle being provided no data when first instantiated or (b) variants
is provided (see below)examples
: an array of objects conforming to the block's schema which provide examples of the data that can be passed to itvariants
: 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 { level: 1 }
and { level: 2 }
as properties
, respectivelyIf schema
is defined, the JSON schema file it refers to:
required
array naming any properties the block will not function without (this MAY be omitted if there are no such properties)configProperties
, which is an array of string
values which MUST be present as a key on the block schema's properties
, i.e. each must name a property the block expects. This field is used to indicate which properties a block considers to be part of 'configuring' how it appears or behaves, rather than data users are creating or viewing via the blocks.If more complex example data is required, a block’s package may also include a file at the root named example-graph.json
, which MUST be a JSON object, and MAY contain one or more of:
entities
entityTypes
links
linkedAggregations
If defined, each field MUST be an array of JSON objects, which MUST conform to the structure defined for each of these resources, as specified in entity definition.
Embedding applications MUST use the constraints set in the block’s schema, if defined, to constrain the data it sends under properties
in the block entity, assuming the embedding application implements the blockEntity
message.
Embedding applications MAY choose to display their own UI to allow users to set any values on the block entity described in configProperties
, and could allow users to set values for them for all blocks of a type (rather than on a per-block instance basis)
An entity is a combination of identifying fields and properties.
The identifying fields are fixed and specified in this document, and appear as keys at the root of the entity data object.
The properties can be any JSON object which is valid for the entity’s type. This object is nested under a properties
key on the entity data object.
Each entity has an entity type representing the type of thing that it is. In turn, an entity type has a schema which sets constraints on the permitted properties belonging to an entity.
Links represent connections or relations between entities. In the language of an entity graph, the links are the edges, and the entities are the nodes.
Linked aggregations represent a link from a block to a particular aggregation of entities, e.g. the top 10 entities of type Person, sorted by a particular property.
When embedding applications pass entities to blocks, whether the block entity or any other:
entityId
identifying the entity, which MUST be a string.entityTypeId
identifying the entity type the entity belongs to, which MUST be a string (if defined).properties
field if the block has defined any required
properties in its schema, and otherwise MAY be omitted. If present, it:
i.e. any entity MUST conform to the following schema:
{
"$id": "https://blockprotocol.org/types/services/graph/entity",
"title": "Entity",
"type": "object",
"properties": {
"entityId": {
"type": "string"
},
"entityTypeId": {
"type": "string"
},
"properties": {
"$ref": "https://example.com/path-to-the-entity-schema"
}
},
"required": ["entityId"]
}
Embedding applications SHOULD also provide – via the entityTypes
message – an entity type for each entity they provide the block, including the block entity. Where provided, an entity type:
entityTypeId
identifying the type, which MUST be a stringschema
, a JSON Schema object defining the constraints on the data in the entity’s properties
field, which
type: "object"
i.e. entity types MUST conform to the following JSON schema:
{
"$id": "https://blockprotocol.org/types/services/graph/entity-type",
"title": "EntityType",
"type": "object",
"properties": {
"entityTypeId": {
"type": "string"
},
"schema": {
"$ref": "https://json-schema.org/draft/2020-12/schema"
}
},
"required": ["entityTypeId", "schema"]
}
Blocks SHOULD use any schemas provided in the entityTypes
message to understand the constraints on user input for each entity (e.g. what type(s) of data are valid for each property).
Entities MAY be connected by a link, which:
sourceEntityId
: the entityId
of the source entity, which MUST be a stringdestinationEntityId
: the id of a single entity the link is made to, which MUST be a stringpath
: the path to the field on the source entity this link is conceptually made on, expressed as a JSON path, which MUST be a stringindex
: the position of this link in a list (for where ordering of links is important), which MUST be an integer (if defined), where 0 is the first entry in the listi.e. links MUST conform to the following schema:
{
"$id": "https://blockprotocol.org/types/services/graph/link",
"title": "Link",
"type": "object",
"properties": {
"sourceEntityId": {
"type": "string"
},
"destinationEntityId": {
"type": "string"
},
"path": {
"type": "string"
},
"index": {
"type": "integer",
"inclusiveMinimum": 0
}
},
"required": ["sourceEntityId", "destinationEntityId", "path"]
}
Where the block entity has links from it to other entities:
blockGraph
message which represents the graph with the block entity at the root, the data
for which MUST be an object, which:
linkGroups
key, which contains a list of objects which group the links in the resolved graph (i.e. the edges) by entity and path. Each object:
links
, an array of linkssourceEntityId
, identical across all entries in links
path
, identical across all entries in path
linkedEntities
key, which contains a list of the entities in the resolved graph (i.e. the nodes)depth
key, which indicates the depth to which the graph around the block entity has been resolved.0
. They MAY also include the entities linked from the block entity and their links which is a depth of 1
, and so on.linkGroups
should conform to the following schema:
{
"$id": "https://blockprotocol.org/types/services/graph/link-group",
"title": "LinkGroup",
"type": "object",
"properties": {
"links": {
"type": "array",
"items": {
"$ref": "https://blockprotocol.org/types/services/graph/link"
}
},
"sourceEntityId": {
"type": "string"
},
"path": {
"type": "string"
}
},
"required": ["sourceEntityId", "destinationEntityId", "path"]
}
Blocks may also wish to link an entity to a particular aggregation of entities. To do so, blocks SHOULD create a LinkedAggregationDefinition, which:
sourceEntityId
: the entityId
of the source entityoperation
: an aggregation operation which the embedding application should resolve the link to, following the structure of the operation
objectpath
: the path to the field on the source entity this link is conceptually made on, expressed as a JSON path, which MUST be a stringi.e. a linked aggregation definition must conform to the following schema:
{
"$id": "https://blockprotocol.org/types/services/graph/linked-aggregation-definition",
"title": "LinkedAggregationDefinition",
"type": "object",
"properties": {
"sourceEntityId": {
"type": "string"
},
"path": {
"type": "string"
},
"operation": {
"$ref": "https://blockprotocol.org/types/services/graph/aggregation-operation"
}
},
"required": ["sourceEntityId", "operation", "path"]
}
Blocks must pass an entityId
when making requests in respect of any entity.
Embedding applications should ensure this is stable and unique for any given entity.
Blocks MUST provide entityId
when identifying a particular entity for the purpose of messages under the graph service.
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.
We define additional keywords for use as part of a JSON Schema definition.
We will publish a JSON schema vocabulary and meta-schema which allows for validating these custom keywords.
We also intend to introduce a richer vocabulary for defining entities and links between them in JSON Schema, using composable property types and data types. Please see the RFC.
As well as the core and validation JSON Schema vocabularies:, the following keywords MAY be used at the root of the schema definition:
labelProperty
: a single string
, which MUST be present as a key on the schema's properties
, i.e. it must name a property available on the entity. Where an entity’s schema is provided as part of the entityTypes
message, and it contains a labelProperty
pointing to a defined property on the entity, blocks MUST use the value for that property as a display name for the entity where appropriate.{
"$id": "https://example.com/Person",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "number" }
},
"labelProperty": "name"
}
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 invalidIndividual messages MAY specify only a subset of these codes as being valid for that message.
Messages under the graph service 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 service 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:
blockEntity
: the block entity, conforming to the constraints defined in the block schemaentityTypes
: the entity type definitions for any entities provided to the block in other messagesblockGraph
: the entity graph with the block at
its root, which contains: - linkedEntities
: the entities linked from the
block entity, and from those entities, and so on, resolved to a depth
determined by the embedding application - linkGroups
: the links from the
block entity and the links from any entities in linkedEntities
, grouped by
source entity and link pathlinkedAggregations
: field containing the definitions and results of aggregations linked from the block’s entity - these are special objects which contain an aggregation definition (e.g. in plain English, "top 10 Cars sorted by age, descending") and its results at the time of provisionreadonly
: 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, entity types, and create links. They are named according to the pattern [action][Resource]
, e.g. createEntity
.
As well as the formal service JSON specification, the messages can be sent and received via the @blockprotocol/graph
package.
The messages available for exchange in the graph service are defined in the service'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]
aggregateEntities[block]
aggregateEntitiesResponse[embedder]
createEntityType[block]
createEntityTypeResponse[embedder]
updateEntityType[block]
updateEntityTypeResponse[embedder]
deleteEntityType[block]
deleteEntityTypeResponse[embedder]
getEntityType[block]
getEntityTypeResponse[embedder]
aggregateEntityTypes[block]
aggregateEntityTypesResponse[embedder]
createLink[block]
createLinkResponse[embedder]
updateLink[block]
updateLinkResponse[embedder]
deleteLink[block]
deleteLinkResponse[embedder]
getLink[block]
getLinkResponse[embedder]
createLinkedAggregation[block]
createLinkedAggregationResponse[embedder]
updateLinkedAggregation[block]
updateLinkedAggregationResponse[embedder]
deleteLinkedAggregation[block]
deleteLinkedAggregationResponse[embedder]
getLinkedAggregation[block]
getLinkedAggregationResponse[embedder]
uploadFile[block]
uploadFileResponse[embedder]
blockEntity[embedder]
blockGraph[embedder]
entityTypes[embedder]
linkedAggregations[embedder]
readonly[embedder]
createEntity
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityTypeId | string | yes | The entityTypeId of the type of the entity to create |
properties | object | yes | The properties of the entity to create |
links | link[] | no | Links to create along with the entity |
errorCodes
: nonerespondedToBy
: createEntityResponsesentOnInitialization
: false
createEntityResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
respondedToBy
: nonesentOnInitialization
: false
updateEntity
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityId | string | yes | The entityId of the entity to update |
properties | object | yes | The new properties object to assign to the entity |
errorCodes
: nonerespondedToBy
: updateEntityResponsesentOnInitialization
: false
updateEntityResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
deleteEntity
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityId | string | yes | The entityId of the entity to delete |
errorCodes
: nonerespondedToBy
: deleteEntityResponsesentOnInitialization
: false
deleteEntityResponse
source
: embedderdata
: boolean
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
getEntity
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityId | string | yes | The entityId of the entity to retrieve |
errorCodes
: nonerespondedToBy
: getEntityResponsesentOnInitialization
: false
getEntityResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
aggregateEntities
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
operation | aggregation-operation | yes | The aggregation operation to apply |
errorCodes
: nonerespondedToBy
: aggregateEntitiesResponsesentOnInitialization
: false
aggregateEntitiesResponse
source
: embedderdata
: object
property | type | required | description |
---|---|---|---|
operation | aggregation-operation | yes | The aggregation operation that was applied to generate the results, including any defaults set by the application. |
results | entity[] | yes | The entities returned by the aggregation operation |
errorCodes
: FORBIDDEN
, INVALID_INPUT
respondedToBy
: nonesentOnInitialization
: false
createEntityType
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
schema | object | yes | The schema of the entity type to create |
errorCodes
: nonerespondedToBy
: createEntityTypeResponsesentOnInitialization
: false
createEntityTypeResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
respondedToBy
: nonesentOnInitialization
: false
updateEntityType
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityTypeId | string | yes | The entityTypeId of the entity to update |
schema | object | yes | The new schema to assign to the entity type |
errorCodes
: nonerespondedToBy
: updateEntityTypeResponsesentOnInitialization
: false
updateEntityTypeResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
deleteEntityType
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityTypeId | string | yes | The entityTypeId of the entityType to delete |
errorCodes
: nonerespondedToBy
: deleteEntityTypeResponsesentOnInitialization
: false
deleteEntityTypeResponse
source
: embedderdata
: boolean
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
getEntityType
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
entityTypeId | string | yes | The entityTypeId of the entity type to retrieve |
errorCodes
: nonerespondedToBy
: getEntityTypeResponsesentOnInitialization
: false
getEntityTypeResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
aggregateEntityTypes
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
operation | aggregation-operation | yes | The aggregation operation to apply |
errorCodes
: nonerespondedToBy
: aggregateEntityTypesResponsesentOnInitialization
: false
aggregateEntityTypesResponse
source
: embedderdata
: object
property | type | required | description |
---|---|---|---|
operation | aggregation-operation | yes | The aggregation operation that was applied to generate the results, including any defaults set by the application. |
results | entity-type[] | yes | The entity types returned by the aggregation operation |
errorCodes
: FORBIDDEN
, INVALID_INPUT
respondedToBy
: nonesentOnInitialization
: false
createLink
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
sourceEntityId | string | yes | The entityId of the source entity for the link |
destinationEntityId | string | yes | The entityId of the destination entity for the link |
index | number | no | The position of this link in an ordered list of links, where ordering is important. An index of 0 represents the first position in the list. |
path | string | yes | The path or field on the entity where this link is made (e.g. 'friend', 'employer') |
errorCodes
: nonerespondedToBy
: createLinkResponsesentOnInitialization
: false
createLinkResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
respondedToBy
: nonesentOnInitialization
: false
updateLink
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
linkId | string | yes | The linkId of the entity to update |
index | number | yes | The new index the link should occupy in an ordered list of links |
errorCodes
: nonerespondedToBy
: updateLinkResponsesentOnInitialization
: false
updateLinkResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
deleteLink
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
linkId | string | yes | The linkId of the link to delete |
errorCodes
: nonerespondedToBy
: deleteLinkResponsesentOnInitialization
: false
deleteLinkResponse
source
: embedderdata
: boolean
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
getLink
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
linkId | string | yes | The linkId of the link to retrieve |
errorCodes
: nonerespondedToBy
: getLinkResponsesentOnInitialization
: false
getLinkResponse
source
: embeddererrorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
createLinkedAggregation
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
sourceEntityId | string | yes | The entityId of the source entity of this linked aggregation |
operation | aggregation-operation | yes | The aggregation operation to apply |
path | string | yes | The path or field on the entity where this linked aggregation is made (e.g. 'rows') |
errorCodes
: nonerespondedToBy
: createLinkedAggregationResponsesentOnInitialization
: false
createLinkedAggregationResponse
source
: embedderdata
: object
property | type | required | description |
---|---|---|---|
linkedAggregation | linked-aggregation-definition | yes | The created linked aggregation |
errorCodes
: FORBIDDEN
, INVALID_INPUT
respondedToBy
: nonesentOnInitialization
: false
updateLinkedAggregation
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
aggregationId | string | yes | The aggregationId of the linked aggregation to update |
operation | aggregation-operation | yes | The new aggregation operation |
errorCodes
: nonerespondedToBy
: updateLinkedAggregationResponsesentOnInitialization
: false
updateLinkedAggregationResponse
source
: embedderdata
: object
property | type | required | description |
---|---|---|---|
linkedAggregation | linked-aggregation-definition | yes | The updated linked aggregation definition |
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
deleteLinkedAggregation
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
aggregationId | string | yes | The aggregationId of the linked aggregation to delete |
errorCodes
: nonerespondedToBy
: deleteLinkedAggregationResponsesentOnInitialization
: false
deleteLinkedAggregationResponse
source
: embedderdata
: boolean
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
getLinkedAggregation
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
aggregationId | string | yes | The aggregationId of the linkedAggregation to retrieve |
errorCodes
: nonerespondedToBy
: getLinkedAggregationResponsesentOnInitialization
: false
getLinkedAggregationResponse
source
: embedderdata
: object
property | type | required | description |
---|---|---|---|
linkedAggregation | linked-aggregation | yes | The retrieved linked aggregation |
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
uploadFile
source
: blockdata
: object
property | type | required | description |
---|---|---|---|
file | no | The file blob, if being uploaded directly. Either 'url' or 'file' is required. | |
url | string | no | The URL to take the file from, if not being uploaded directly. Either 'url' or 'file' is required. |
mediaType | string | yes | The type of media file this is |
errorCodes
: nonerespondedToBy
: uploadFileResponsesentOnInitialization
: false
uploadFileResponse
source
: embedderdata
: object
property | type | required | description |
---|---|---|---|
entityId | yes | The entityId of the entity storing metadata about the file. | |
url | string | yes | The URL the file is now being served from. |
mediaType | string | yes | The type of media file this is |
errorCodes
: FORBIDDEN
, INVALID_INPUT
, NOT_FOUND
respondedToBy
: nonesentOnInitialization
: false
blockEntity
source
: embedderdata
: entity
errorCodes
: nonerespondedToBy
: nonesentOnInitialization
: true
blockGraph
source
: embedderdata
: object
property | type | required | description |
---|---|---|---|
linkedEntities | entity[] | yes | The entities linked from the block entity, and potentially entities linked from them, and so on, depending on depth. |
linkGroups | link-group[] | yes | The links attached to the block or the entities provided in linkedEntities, grouped by entity and path. |
depth | integer | yes | The number of links that will be followed from the block when resolving the graph. |
errorCodes
: nonerespondedToBy
: nonesentOnInitialization
: true
entityTypes
source
: embedderdata
: entity-type[]
errorCodes
: nonerespondedToBy
: nonesentOnInitialization
: true
linkedAggregations
source
: embeddererrorCodes
: nonerespondedToBy
: nonesentOnInitialization
: true
readonly
source
: embedderdata
: boolean
errorCodes
: nonerespondedToBy
: nonesentOnInitialization
: true
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.