Skip to content

Data Modeling

Vendia automatically converts your JSON Schema-based data model into a production-grade, distributed application composed of serverless public cloud resources, complete with GraphQL APIs.

Much as a conventional database turns a CREATE_TABLE definition into a single, centralized database, Vendia turns your JSON Schema-based data model into a distributed, decentralized database…and all the platform services needed to make that data available and useful in the cloud.

This guide explains how JSON schema types are converted into the GraphQL types that each node in your Uni can access, update, and subscribe to.

Defining “Entities”

Each key/value pair in the top-level "properties" object of your JSON schema will be converted into a Vendia “Entity”. Here’s an example of a minimal JSON schema with a single entity called “Product”:

{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://vendia.com/schemas/sample.schema.json",
"title": "Acme Uni Schema",
"description": "Defines data model for Acme's Product Uni",
"type": "object",
"properties": {
"Product": { <------------------------------ This is an entity!
"description": "Acme Product Line",
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { <--------------------------- This is a field on the Product entity
"type" : "string"
}
}
}
}
}
}

Entities are special in Vendia Share. Entities receive their own GraphQL APIs for CRUD operations (e.g. addProduct, getProduct, listProductItems) and entities of type "array" can be indexed to enable efficient data retrieval (More on indexes below).

Only immediate children of your JSON schema’s top-level "properties" object will be converted to Entities. In the example above, the Product entity is an "array" type with items of type "object" - the object definition, in turn, has a "name" field.

Entity fields can contain complex data types (e.g. "object", "array") in addition to scalar data types (e.g. "string", "number", "integer"), but arrays of data nested within entities will not have their own APIs and cannot be indexed. Data models that require large amounts of total storage or very high cardinality for nested arrays should be rewritten to make these appear as top-level Entity arrays in order to expose them to indexing and sharding in the database.

About scalar types

Strings, numbers, booleans… We often refer to these fundamental data types as “scalars”. Your Uni’s data model might be organized into any number of entities and these entities might consist of complex structures of nested arrays and objects, but ultimately you’re going to need scalar types to store meaningful data.

Vendia Share supports any scalar type that can be defined in JSON schema.

Some notes on the particulars:

  • Strings, booleans, and integers convert directly to the corresponding GraphQL types.
  • JSON Schema number types will convert to GraphQL float types.
  • Floats have a precision of 16 decimal places and a range of 2.2250738585072014e-308 to 1.7976931348623157e+308.
  • The allowable range for integer types is -2147483648 through 2147483647.
  • JSON Schema date, time, and datetimes types will convert to system-specific scalars in GraphQL (e.g., AWS_DATE for GraphQL queries made on AWS).

Uni JSON schema example

The following is a sample data model for a product catalog and orders, written as JSON schema

Sample Uni schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://vendia.com/schemas/sample.schema.json",
"title": "Acme Uni Schema",
"description": "Defines data model for Acme's Product Uni",
"type": "object",
"properties": {
"ContactInfo": {
"type": "object",
"description": "Global setting that records general purpose contact info for the chain as a whole",
"properties": {
"addressLine1": {
"type": "string"
},
"addressLine2": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"zipCode": {
"type": "string"
}
}
},
"Participant": {
"description": "Blockchain participant names",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"Order": {
"description": "3b11 replenishment order",
"type": "array",
"items": {
"type": "object",
"properties": {
"orderId": {
"description": "The unique identifier for an order",
"type": "string"
},
"owner": {
"description": "Role who initially authorized order",
"type": "string"
},
"creationTimestamp": {
"description": "Timestamp when order was initially created",
"type": "string"
},
"orderContent": {
"description": "Product IDs in this order",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
}
},
"required": ["orderId", "owner", "creationTimestamp", "orderContent"]
},
"minItems": 0,
"uniqueItems": true
},
"ShipmentMessage": {
"description": "Update to shipping status of an order",
"type": "array",
"items": {
"type": "object",
"properties": {
"orderId": {
"description": "The unique identifier for all messages related to this shipment",
"type": "string"
},
"carrier": {
"description": "The carrier handling the shipment",
"type": "string"
},
"timestamp": {
"description": "The arrival time",
"type": "string"
},
"fromAddress": {
"description": "Origin for this shipment update",
"type": "object",
"properties": {
"isInitial": {
"type": "boolean"
},
"contact": {
"type": "string"
},
"streetAddress": {
"type": "string"
},
"city": {
"type": "string"
},
"postalCode": {
"type": "string"
},
"country": {
"type": "string"
}
},
"required": ["streetAddress", "city", "country"]
},
"toAddress": {
"description": "Destination for this shipment update",
"type": "object",
"properties": {
"isFinal": {
"type": "boolean"
},
"contact": {
"type": "string"
},
"streetAddress": {
"type": "string"
},
"city": {
"type": "string"
},
"postalCode": {
"type": "string"
},
"country": {
"type": "string"
}
},
"required": ["streetAddress", "city", "country"]
}
},
"required": [
"orderId",
"carrier",
"timestamp",
"fromAddress",
"toAddress"
]
},
"minItems": 0,
"uniqueItems": true
},
"Product": {
"description": "Acme Product Line",
"type": "array",
"items": {
"type": "object",
"properties": {
"productId": {
"description": "The unique identifier for a product",
"type": "integer"
},
"productName": {
"description": "Name of the product",
"type": "string"
},
"price": {
"description": "The price of the product",
"type": "number",
"exclusiveMinimum": 0
},
"tags": {
"description": "Tags for the product",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"dimensions": {
"type": "object",
"properties": {
"length": {
"type": "number"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
}
},
"required": ["length", "width"]
},
"sales": {
"description": "Sales for the product",
"type": "array",
"items": {
"type": "object",
"properties": {
"start": {
"type": "string"
},
"end": {
"type": "string"
},
"discountPercent": {
"type": "number"
}
},
"required": ["start", "end"]
}
}
},
"required": ["productId", "productName", "price"]
},
"minItems": 1,
"uniqueItems": true
}
},
"required": ["ContactInfo", "Participant", "Product"]
}

Vendia Share will convert the schema above into a GraphQL representation similar to the one below. (Note that this is representative only; details of this translation may vary based on your registration, settings, etc.)

Representative generated GraphQL schema
enum ModelAttributeTypes {
binary
binarySet
bool
list
map
number
numberSet
string
stringSet
_null
}
input ModelBooleanInput {
ne: Boolean
eq: Boolean
attributeExists: Boolean
attributeType: ModelAttributeTypes
}
input ModelFloatInput {
ne: Float
eq: Float
le: Float
lt: Float
ge: Float
gt: Float
between: [Float]
attributeExists: Boolean
attributeType: ModelAttributeTypes
}
input ModelIDInput {
ne: ID
eq: ID
attributeExists: Boolean
attributeType: ModelAttributeTypes
}
input ModelIntInput {
ne: Int
eq: Int
le: Int
lt: Int
ge: Int
gt: Int
between: [Int]
attributeExists: Boolean
attributeType: ModelAttributeTypes
}
input ModelSizeInput {
ne: Int
eq: Int
le: Int
lt: Int
ge: Int
gt: Int
between: [Int]
}
enum ModelSortDirection {
ASC
DESC
}
input ModelStringInput {
ne: String
eq: String
le: String
lt: String
ge: String
gt: String
contains: String
notContains: String
between: [String]
beginsWith: String
attributeExists: Boolean
attributeType: ModelAttributeTypes
size: ModelSizeInput
}
type Transaction {
tx_id: String!
tx_version: String!
submission_time: String!
node_owner: String!
}
type Transaction_Result {
error: String
result: Transaction
}
input ContactInfoConditionInput {
addressLine1: ModelStringInput
addressLine2: ModelStringInput
city: ModelStringInput
state: ModelStringInput
zipCode: ModelStringInput
and: [ContactInfoConditionInput]
or: [ContactInfoConditionInput]
not: ContactInfoConditionInput
}
input ContactInfoFilterInput {
addressLine1: ModelStringInput
addressLine2: ModelStringInput
city: ModelStringInput
state: ModelStringInput
zipCode: ModelStringInput
and: [ContactInfoFilterInput]
or: [ContactInfoFilterInput]
not: ContactInfoFilterInput
}
type ContactInfo {
addressLine1: String
addressLine2: String
city: String
state: String
zipCode: String
}
type ContactInfo_Result {
error: String
result: ContactInfo
}
input ContactInfoInput {
addressLine1: String
addressLine2: String
city: String
state: String
zipCode: String
}
input ParticipantConditionInput {
Participant: ModelStringInput
and: [ParticipantConditionInput]
or: [ParticipantConditionInput]
not: ParticipantConditionInput
}
input ParticipantFilterInput {
id: ModelIDInput
Participant: ModelStringInput
and: [ParticipantFilterInput]
or: [ParticipantFilterInput]
not: ParticipantFilterInput
}
type Participant {
id: ID!
Participant: String!
}
type Participant_Result {
error: String
result: Participant
}
type ParticipantConnection {
Participants: [Participant]
nextToken: String
}
input OrderConditionInput {
orderId: ModelStringInput
owner: ModelStringInput
creationTimestamp: ModelStringInput
orderContent: ModelStringInput
and: [OrderConditionInput]
or: [OrderConditionInput]
not: OrderConditionInput
}
input OrderFilterInput {
id: ModelIDInput
orderId: ModelStringInput
owner: ModelStringInput
creationTimestamp: ModelStringInput
orderContent: ModelStringInput
and: [OrderFilterInput]
or: [OrderFilterInput]
not: OrderFilterInput
}
type Order {
id: ID!
orderId: String!
owner: String!
creationTimestamp: String!
orderContent: [String]!
}
type Order_Result {
error: String
result: Order
}
type OrderConnection {
Orders: [Order]
nextToken: String
}
input OrderInput {
orderId: String!
owner: String!
creationTimestamp: String!
orderContent: [String]!
}
input ShipmentMessageConditionInput {
orderId: ModelStringInput
carrier: ModelStringInput
timestamp: ModelStringInput
fromAddress: fromAddressConditionInput
toAddress: toAddressConditionInput
and: [ShipmentMessageConditionInput]
or: [ShipmentMessageConditionInput]
not: ShipmentMessageConditionInput
}
input ShipmentMessageFilterInput {
id: ModelIDInput
orderId: ModelStringInput
carrier: ModelStringInput
timestamp: ModelStringInput
fromAddress: fromAddressFilterInput
toAddress: toAddressFilterInput
and: [ShipmentMessageFilterInput]
or: [ShipmentMessageFilterInput]
not: ShipmentMessageFilterInput
}
type ShipmentMessage {
id: ID!
orderId: String!
carrier: String!
timestamp: String!
fromAddress: fromAddress
toAddress: toAddress
}
type ShipmentMessage_Result {
error: String
result: ShipmentMessage
}
type ShipmentMessageConnection {
ShipmentMessages: [ShipmentMessage]
nextToken: String
}
input ShipmentMessageInput {
orderId: String!
carrier: String!
timestamp: String!
fromAddress: fromAddressInput
toAddress: toAddressInput
}
input ProductConditionInput {
productId: ModelIntInput
productName: ModelStringInput
price: ModelFloatInput
tags: ModelStringInput
dimensions: dimensionsConditionInput
sales_element: sales_elementConditionInput
and: [ProductConditionInput]
or: [ProductConditionInput]
not: ProductConditionInput
}
input ProductFilterInput {
id: ModelIDInput
productId: ModelIntInput
productName: ModelStringInput
price: ModelFloatInput
tags: ModelStringInput
dimensions: dimensionsFilterInput
sales_element: sales_elementFilterInput
and: [ProductFilterInput]
or: [ProductFilterInput]
not: ProductFilterInput
}
type Product {
id: ID!
productId: Int!
productName: String!
price: Float!
tags: [String]
dimensions: dimensions
sales: [sales_element]
}
type Product_Result {
error: String
result: Product
}
type ProductConnection {
Products: [Product]
nextToken: String
}
input ProductInput {
productId: Int!
productName: String!
price: Float!
tags: [String]
dimensions: dimensionsInput
sales: [sales_elementInput]
}
input _BlockConditionInput {
BlockId: ModelStringInput
RedactedBlockHash: ModelStringInput
PreviousBlockId: ModelStringInput
PreviousRedactedBlockHash: ModelStringInput
PreconditionsMet: ModelStringInput
CommitTime: ModelStringInput
_TX_element: _TX_elementConditionInput
and: [_BlockConditionInput]
or: [_BlockConditionInput]
not: _BlockConditionInput
}
input _BlockFilterInput {
id: ModelIDInput
BlockId: ModelStringInput
RedactedBlockHash: ModelStringInput
PreviousBlockId: ModelStringInput
PreviousRedactedBlockHash: ModelStringInput
PreconditionsMet: ModelStringInput
CommitTime: ModelStringInput
_TX_element: _TX_elementFilterInput
and: [_BlockFilterInput]
or: [_BlockFilterInput]
not: _BlockFilterInput
}
type _Block {
id: ID!
BlockId: String!
RedactedBlockHash: String!
PreviousBlockId: String
PreviousRedactedBlockHash: String
PreconditionsMet: String
CommitTime: String!
_TX: [_TX_element]!
}
type _Block_Result {
error: String
result: _Block
}
type _BlockConnection {
_Blocks: [_Block]
nextToken: String
}
input _BlockInput {
BlockId: String!
RedactedBlockHash: String!
PreviousBlockId: String
PreviousRedactedBlockHash: String
PreconditionsMet: String
CommitTime: String!
_TX: [_TX_elementInput]!
}
input _SecureMessageConditionInput {
Participants: ModelStringInput
Message: ModelStringInput
and: [_SecureMessageConditionInput]
or: [_SecureMessageConditionInput]
not: _SecureMessageConditionInput
}
input _SecureMessageFilterInput {
id: ModelIDInput
Participants: ModelStringInput
Message: ModelStringInput
and: [_SecureMessageFilterInput]
or: [_SecureMessageFilterInput]
not: _SecureMessageFilterInput
}
type _SecureMessage {
id: ID!
Participants: [String]!
Message: String!
}
type _SecureMessage_Result {
error: String
result: _SecureMessage
}
type _SecureMessageConnection {
_SecureMessages: [_SecureMessage]
nextToken: String
}
input _SecureMessageInput {
Participants: [String]!
Message: String!
}
input _ContractConditionInput {
InvocationUID: ModelStringInput
Function: ModelStringInput
Arguments_element: Arguments_elementConditionInput
Results_element: Results_elementConditionInput
and: [_ContractConditionInput]
or: [_ContractConditionInput]
not: _ContractConditionInput
}
input _ContractFilterInput {
id: ModelIDInput
InvocationUID: ModelStringInput
Function: ModelStringInput
Arguments_element: Arguments_elementFilterInput
Results_element: Results_elementFilterInput
and: [_ContractFilterInput]
or: [_ContractFilterInput]
not: _ContractFilterInput
}
type _Contract {
id: ID!
InvocationUID: String!
Function: String!
Arguments: [Arguments_element]
Results: [Results_element]
}
type _Contract_Result {
error: String
result: _Contract
}
type _ContractConnection {
_Contracts: [_Contract]
nextToken: String
}
input _ContractInput {
InvocationUID: String!
Function: String!
Arguments: [Arguments_elementInput]
Results: [Results_elementInput]
}
input _SettingsConditionInput {
blockReportWebhooks: ModelStringInput
blockReportEmails: ModelStringInput
aws_blockReportSQSQueues: ModelStringInput
aws_blockReportLambdas: ModelStringInput
aws_blockReportFirehoses: ModelStringInput
aws_SQSIngressAccounts: ModelStringInput
aws_S3ReadAccounts: ModelStringInput
aws_LambdaIngressAccounts: ModelStringInput
_ResourceMapKeys: ModelStringInput
_ResourceMapValues: ModelStringInput
graphqlAuth: graphqlAuthConditionInput
aws_DataDogMonitoring: aws_DataDogMonitoringConditionInput
and: [_SettingsConditionInput]
or: [_SettingsConditionInput]
not: _SettingsConditionInput
}
input _SettingsFilterInput {
blockReportWebhooks: ModelStringInput
blockReportEmails: ModelStringInput
aws_blockReportSQSQueues: ModelStringInput
aws_blockReportLambdas: ModelStringInput
aws_blockReportFirehoses: ModelStringInput
aws_SQSIngressAccounts: ModelStringInput
aws_S3ReadAccounts: ModelStringInput
aws_LambdaIngressAccounts: ModelStringInput
_ResourceMapKeys: ModelStringInput
_ResourceMapValues: ModelStringInput
graphqlAuth: graphqlAuthFilterInput
aws_DataDogMonitoring: aws_DataDogMonitoringFilterInput
and: [_SettingsFilterInput]
or: [_SettingsFilterInput]
not: _SettingsFilterInput
}
type _Settings {
blockReportWebhooks: [String]
blockReportEmails: [String]
aws_blockReportSQSQueues: [String]
aws_blockReportLambdas: [String]
aws_blockReportFirehoses: [String]
aws_SQSIngressAccounts: [String]
aws_S3ReadAccounts: [String]
aws_LambdaIngressAccounts: [String]
_ResourceMapKeys: [String]
_ResourceMapValues: [String]
graphqlAuth: graphqlAuth
aws_DataDogMonitoring: aws_DataDogMonitoring
}
type _Settings_Result {
error: String
result: _Settings
}
input _SettingsInput {
blockReportWebhooks: [String]
blockReportEmails: [String]
aws_blockReportSQSQueues: [String]
aws_blockReportLambdas: [String]
aws_blockReportFirehoses: [String]
aws_SQSIngressAccounts: [String]
aws_S3ReadAccounts: [String]
aws_LambdaIngressAccounts: [String]
_ResourceMapKeys: [String]
_ResourceMapValues: [String]
graphqlAuth: graphqlAuthInput
aws_DataDogMonitoring: aws_DataDogMonitoringInput
}
input _UniInfoConditionInput {
name: ModelStringInput
schema: ModelStringInput
status: ModelStringInput
created: ModelStringInput
nodes_element: nodes_elementConditionInput
localNodeName: ModelStringInput
and: [_UniInfoConditionInput]
or: [_UniInfoConditionInput]
not: _UniInfoConditionInput
}
input _UniInfoFilterInput {
name: ModelStringInput
schema: ModelStringInput
status: ModelStringInput
created: ModelStringInput
nodes_element: nodes_elementFilterInput
localNodeName: ModelStringInput
and: [_UniInfoFilterInput]
or: [_UniInfoFilterInput]
not: _UniInfoFilterInput
}
type _UniInfo {
name: String!
schema: String!
status: String
created: String!
nodes: [nodes_element]!
localNodeName: String
}
type _UniInfo_Result {
error: String
result: _UniInfo
}
input _UniInfoInput {
name: String!
schema: String!
status: String
created: String!
nodes: [nodes_elementInput]!
localNodeName: String
}
input _DeploymentInfoConditionInput {
DeploymentDate: ModelStringInput
ConsensusDefinitionHash: ModelStringInput
VersionTag: ModelStringInput
and: [_DeploymentInfoConditionInput]
or: [_DeploymentInfoConditionInput]
not: _DeploymentInfoConditionInput
}
input _DeploymentInfoFilterInput {
id: ModelIDInput
DeploymentDate: ModelStringInput
ConsensusDefinitionHash: ModelStringInput
VersionTag: ModelStringInput
and: [_DeploymentInfoFilterInput]
or: [_DeploymentInfoFilterInput]
not: _DeploymentInfoFilterInput
}
type _DeploymentInfo {
id: ID!
DeploymentDate: String!
ConsensusDefinitionHash: String!
VersionTag: String!
}
type _DeploymentInfo_Result {
error: String
result: _DeploymentInfo
}
type _DeploymentInfoConnection {
_DeploymentInfos: [_DeploymentInfo]
nextToken: String
}
input _DeploymentInfoInput {
DeploymentDate: String!
ConsensusDefinitionHash: String!
VersionTag: String!
}
input _ParticipantAddsAllowedConditionInput {
_ParticipantAddsAllowed: ModelBooleanInput
and: [_ParticipantAddsAllowedConditionInput]
or: [_ParticipantAddsAllowedConditionInput]
not: _ParticipantAddsAllowedConditionInput
}
input fromAddressConditionInput {
isInitial: ModelBooleanInput
contact: ModelStringInput
streetAddress: ModelStringInput
city: ModelStringInput
postalCode: ModelStringInput
country: ModelStringInput
and: [fromAddressConditionInput]
or: [fromAddressConditionInput]
not: fromAddressConditionInput
}
input fromAddressFilterInput {
isInitial: ModelBooleanInput
contact: ModelStringInput
streetAddress: ModelStringInput
city: ModelStringInput
postalCode: ModelStringInput
country: ModelStringInput
and: [fromAddressFilterInput]
or: [fromAddressFilterInput]
not: fromAddressFilterInput
}
type fromAddress {
isInitial: Boolean
contact: String
streetAddress: String!
city: String!
postalCode: String
country: String!
}
type fromAddress_Result {
error: String
result: fromAddress
}
input toAddressConditionInput {
isFinal: ModelBooleanInput
contact: ModelStringInput
streetAddress: ModelStringInput
city: ModelStringInput
postalCode: ModelStringInput
country: ModelStringInput
and: [toAddressConditionInput]
or: [toAddressConditionInput]
not: toAddressConditionInput
}
input toAddressFilterInput {
isFinal: ModelBooleanInput
contact: ModelStringInput
streetAddress: ModelStringInput
city: ModelStringInput
postalCode: ModelStringInput
country: ModelStringInput
and: [toAddressFilterInput]
or: [toAddressFilterInput]
not: toAddressFilterInput
}
type toAddress {
isFinal: Boolean
contact: String
streetAddress: String!
city: String!
postalCode: String
country: String!
}
type toAddress_Result {
error: String
result: toAddress
}
input dimensionsConditionInput {
length: ModelFloatInput
width: ModelFloatInput
height: ModelFloatInput
and: [dimensionsConditionInput]
or: [dimensionsConditionInput]
not: dimensionsConditionInput
}
input dimensionsFilterInput {
length: ModelFloatInput
width: ModelFloatInput
height: ModelFloatInput
and: [dimensionsFilterInput]
or: [dimensionsFilterInput]
not: dimensionsFilterInput
}
type dimensions {
length: Float!
width: Float!
height: Float
}
type dimensions_Result {
error: String
result: dimensions
}
input sales_elementConditionInput {
start: ModelStringInput
end: ModelStringInput
discountPercent: ModelFloatInput
and: [sales_elementConditionInput]
or: [sales_elementConditionInput]
not: sales_elementConditionInput
}
input sales_elementFilterInput {
start: ModelStringInput
end: ModelStringInput
discountPercent: ModelFloatInput
and: [sales_elementFilterInput]
or: [sales_elementFilterInput]
not: sales_elementFilterInput
}
type sales_element {
start: String!
end: String!
discountPercent: Float
}
type sales_element_Result {
error: String
result: sales_element
}
input _TX_elementConditionInput {
TxId: ModelStringInput
TxHash: ModelStringInput
Owner: ModelStringInput
Sig: ModelStringInput
Version: ModelStringInput
Mutations: ModelStringInput
and: [_TX_elementConditionInput]
or: [_TX_elementConditionInput]
not: _TX_elementConditionInput
}
input _TX_elementFilterInput {
TxId: ModelStringInput
TxHash: ModelStringInput
Owner: ModelStringInput
Sig: ModelStringInput
Version: ModelStringInput
Mutations: ModelStringInput
and: [_TX_elementFilterInput]
or: [_TX_elementFilterInput]
not: _TX_elementFilterInput
}
type _TX_element {
TxId: String!
TxHash: String
Owner: String
Sig: String
Version: String
Mutations: String!
}
type _TX_element_Result {
error: String
result: _TX_element
}
input Arguments_elementConditionInput {
Name: ModelStringInput
Query: ModelStringInput
and: [Arguments_elementConditionInput]
or: [Arguments_elementConditionInput]
not: Arguments_elementConditionInput
}
input Arguments_elementFilterInput {
Name: ModelStringInput
Query: ModelStringInput
and: [Arguments_elementFilterInput]
or: [Arguments_elementFilterInput]
not: Arguments_elementFilterInput
}
type Arguments_element {
Name: String!
Query: String!
}
type Arguments_element_Result {
error: String
result: Arguments_element
}
input Results_elementConditionInput {
Mutation: ModelStringInput
Arguments_element_1: Arguments_element_1ConditionInput
and: [Results_elementConditionInput]
or: [Results_elementConditionInput]
not: Results_elementConditionInput
}
input Results_elementFilterInput {
Mutation: ModelStringInput
Arguments_element_1: Arguments_element_1FilterInput
and: [Results_elementFilterInput]
or: [Results_elementFilterInput]
not: Results_elementFilterInput
}
type Results_element {
Mutation: String!
Arguments: [Arguments_element_1]
}
type Results_element_Result {
error: String
result: Results_element
}
input Arguments_element_1ConditionInput {
Name: ModelStringInput
Type: ModelStringInput
ResultPath: ModelStringInput
and: [Arguments_element_1ConditionInput]
or: [Arguments_element_1ConditionInput]
not: Arguments_element_1ConditionInput
}
input Arguments_element_1FilterInput {
Name: ModelStringInput
Type: ModelStringInput
ResultPath: ModelStringInput
and: [Arguments_element_1FilterInput]
or: [Arguments_element_1FilterInput]
not: Arguments_element_1FilterInput
}
type Arguments_element_1 {
Name: String!
Type: String!
ResultPath: String
}
type Arguments_element_1_Result {
error: String
result: Arguments_element_1
}
input graphqlAuthConditionInput {
authorizerType: ModelStringInput
authorizerArn: ModelStringInput
and: [graphqlAuthConditionInput]
or: [graphqlAuthConditionInput]
not: graphqlAuthConditionInput
}
input graphqlAuthFilterInput {
authorizerType: ModelStringInput
authorizerArn: ModelStringInput
and: [graphqlAuthFilterInput]
or: [graphqlAuthFilterInput]
not: graphqlAuthFilterInput
}
type graphqlAuth {
authorizerType: String
authorizerArn: String
}
type graphqlAuth_Result {
error: String
result: graphqlAuth
}
input aws_DataDogMonitoringConditionInput {
ddExternalId: ModelStringInput
ddApiKey: ModelStringInput
ddLogEndpoint: ModelStringInput
ddSendLogs: ModelBooleanInput
and: [aws_DataDogMonitoringConditionInput]
or: [aws_DataDogMonitoringConditionInput]
not: aws_DataDogMonitoringConditionInput
}
input aws_DataDogMonitoringFilterInput {
ddExternalId: ModelStringInput
ddApiKey: ModelStringInput
ddLogEndpoint: ModelStringInput
ddSendLogs: ModelBooleanInput
and: [aws_DataDogMonitoringFilterInput]
or: [aws_DataDogMonitoringFilterInput]
not: aws_DataDogMonitoringFilterInput
}
type aws_DataDogMonitoring {
ddExternalId: String
ddApiKey: String
ddLogEndpoint: String
ddSendLogs: Boolean
}
type aws_DataDogMonitoring_Result {
error: String
result: aws_DataDogMonitoring
}
input nodes_elementConditionInput {
name: ModelStringInput
userId: ModelStringInput
userEmail: ModelStringInput
description: ModelStringInput
status: ModelStringInput
region: ModelStringInput
vendiaAccount: vendiaAccountConditionInput
and: [nodes_elementConditionInput]
or: [nodes_elementConditionInput]
not: nodes_elementConditionInput
}
input nodes_elementFilterInput {
name: ModelStringInput
userId: ModelStringInput
userEmail: ModelStringInput
description: ModelStringInput
status: ModelStringInput
region: ModelStringInput
vendiaAccount: vendiaAccountFilterInput
and: [nodes_elementFilterInput]
or: [nodes_elementFilterInput]
not: nodes_elementFilterInput
}
type nodes_element {
name: String!
userId: String!
userEmail: String
description: String
status: String
region: String!
vendiaAccount: vendiaAccount
}
type nodes_element_Result {
error: String
result: nodes_element
}
input vendiaAccountConditionInput {
csp: ModelStringInput
accountId: ModelStringInput
userId: ModelStringInput
org: ModelStringInput
and: [vendiaAccountConditionInput]
or: [vendiaAccountConditionInput]
not: vendiaAccountConditionInput
}
input vendiaAccountFilterInput {
csp: ModelStringInput
accountId: ModelStringInput
userId: ModelStringInput
org: ModelStringInput
and: [vendiaAccountFilterInput]
or: [vendiaAccountFilterInput]
not: vendiaAccountFilterInput
}
type vendiaAccount {
csp: String!
accountId: String!
userId: String
org: String
}
type vendiaAccount_Result {
error: String
result: vendiaAccount
}
type Query {
getContactInfo: ContactInfo
getParticipant(id: ID!): Participant
listParticipants(
filter: ParticipantFilterInput
limit: Int
nextToken: String
): ParticipantConnection
getOrder(id: ID!): Order
listOrders(
filter: OrderFilterInput
limit: Int
nextToken: String
): OrderConnection
getShipmentMessage(id: ID!): ShipmentMessage
listShipmentMessages(
filter: ShipmentMessageFilterInput
limit: Int
nextToken: String
): ShipmentMessageConnection
getProduct(id: ID!): Product
listProducts(
filter: ProductFilterInput
limit: Int
nextToken: String
): ProductConnection
get_Block(id: ID!): _Block
list_Blocks(
filter: _BlockFilterInput
limit: Int
nextToken: String
): _BlockConnection
get_SecureMessage(id: ID!): _SecureMessage
list_SecureMessages(
filter: _SecureMessageFilterInput
limit: Int
nextToken: String
): _SecureMessageConnection
get_Contract(id: ID!): _Contract
list_Contracts(
filter: _ContractFilterInput
limit: Int
nextToken: String
): _ContractConnection
get_Settings: _Settings
get_UniInfo: _UniInfo
get_DeploymentInfo(id: ID!): _DeploymentInfo
list_DeploymentInfos(
filter: _DeploymentInfoFilterInput
limit: Int
nextToken: String
): _DeploymentInfoConnection
get_ParticipantAddsAllowed: Boolean
}
type Mutation {
createContactInfo_async(input: ContactInfoInput!): Transaction_Result
updateContactInfo_async(
input: ContactInfoInput!
condition: ContactInfoConditionInput
): Transaction_Result
addParticipant_async(input: String!): Transaction_Result
updateParticipant_async(
id: ID!
input: String!
condition: ParticipantConditionInput
): Transaction_Result
removeParticipant_async(
id: ID!
condition: ParticipantConditionInput
): Transaction_Result
addOrder_async(input: OrderInput!): Transaction_Result
updateOrder_async(
id: ID!
input: OrderInput!
condition: OrderConditionInput
): Transaction_Result
removeOrder_async(id: ID!, condition: OrderConditionInput): Transaction_Result
addShipmentMessage_async(input: ShipmentMessageInput!): Transaction_Result
updateShipmentMessage_async(
id: ID!
input: ShipmentMessageInput!
condition: ShipmentMessageConditionInput
): Transaction_Result
removeShipmentMessage_async(
id: ID!
condition: ShipmentMessageConditionInput
): Transaction_Result
addProduct_async(input: ProductInput!): Transaction_Result
updateProduct_async(
id: ID!
input: ProductInput!
condition: ProductConditionInput
): Transaction_Result
removeProduct_async(
id: ID!
condition: ProductConditionInput
): Transaction_Result
add_Block_async(input: _BlockInput!): Transaction_Result
update_Block_async(
id: ID!
input: _BlockInput!
condition: _BlockConditionInput
): Transaction_Result
remove_Block_async(
id: ID!
condition: _BlockConditionInput
): Transaction_Result
add_SecureMessage_async(input: _SecureMessageInput!): Transaction_Result
update_SecureMessage_async(
id: ID!
input: _SecureMessageInput!
condition: _SecureMessageConditionInput
): Transaction_Result
remove_SecureMessage_async(
id: ID!
condition: _SecureMessageConditionInput
): Transaction_Result
add_Contract_async(input: _ContractInput!): Transaction_Result
update_Contract_async(
id: ID!
input: _ContractInput!
condition: _ContractConditionInput
): Transaction_Result
remove_Contract_async(
id: ID!
condition: _ContractConditionInput
): Transaction_Result
create_Settings_async(input: _SettingsInput!): Transaction_Result
update_Settings_async(
input: _SettingsInput!
condition: _SettingsConditionInput
): Transaction_Result
delete_Settings_async(condition: _SettingsConditionInput): Transaction_Result
create_UniInfo_async(input: _UniInfoInput!): Transaction_Result
update_UniInfo_async(
input: _UniInfoInput!
condition: _UniInfoConditionInput
): Transaction_Result
delete_UniInfo_async(condition: _UniInfoConditionInput): Transaction_Result
add_DeploymentInfo_async(input: _DeploymentInfoInput!): Transaction_Result
update_DeploymentInfo_async(
id: ID!
input: _DeploymentInfoInput!
condition: _DeploymentInfoConditionInput
): Transaction_Result
remove_DeploymentInfo_async(
id: ID!
condition: _DeploymentInfoConditionInput
): Transaction_Result
create_ParticipantAddsAllowed_async(input: Boolean!): Transaction_Result
update_ParticipantAddsAllowed_async(
input: Boolean!
condition: _ParticipantAddsAllowedConditionInput
): Transaction_Result
delete_ParticipantAddsAllowed_async(
condition: _ParticipantAddsAllowedConditionInput
): Transaction_Result
}
type Schema {
query: Query
mutation: Mutation
}
input fromAddressInput {
isInitial: Boolean
contact: String
streetAddress: String!
city: String!
postalCode: String
country: String!
}
input toAddressInput {
isFinal: Boolean
contact: String
streetAddress: String!
city: String!
postalCode: String
country: String!
}
input dimensionsInput {
length: Float!
width: Float!
height: Float
}
input sales_elementInput {
start: String!
end: String!
discountPercent: Float
}
input _TX_elementInput {
TxId: String!
TxHash: String
Owner: String
Sig: String
Version: String
Mutations: String!
}
input Arguments_elementInput {
Name: String!
Query: String!
}
input Results_elementInput {
Mutation: String!
Arguments: [Arguments_element_1Input]
}
input Arguments_element_1Input {
Name: String!
Type: String!
ResultPath: String
}
input graphqlAuthInput {
authorizerType: String
authorizerArn: String
}
input aws_DataDogMonitoringInput {
ddExternalId: String
ddApiKey: String
ddLogEndpoint: String
ddSendLogs: Boolean
}
input nodes_elementInput {
name: String!
userId: String!
userEmail: String
description: String
status: String
region: String!
vendiaAccount: vendiaAccountInput
}
input vendiaAccountInput {
csp: String!
accountId: String!
userId: String
org: String
}

Lastly, here is an example of an initial state (Uni starting point) file that conforms to the schema above:

NOTE: defining an initial state for your Uni is entirely optional! Initial state is only supported in the Share CLI and is only intended for small sets of data.

Sample Uni initial state
{
"ContactInfo": {
"addressLine1": "170 West Tasman Drive",
"city": "San Jose",
"state": "CA",
"zipCode": "95134"
},
"Participant": ["Acme Corporation", "RoadRunner, Inc."],
"Product": [
{
"productId": 123,
"productName": "Server rack",
"price": 99.99,
"tags": ["discount", "requires_installation"],
"dimensions": {
"length": 48,
"width": 36,
"height": 51
}
},
{
"productId": 456,
"productName": "Server_xyz_config",
"price": 555.99
},
{
"productId": 789,
"productName": "Server_abc_config",
"price": 799.99
}
]
}

Runtime validation of GraphQL mutations

Your Uni’s JSON schema can be used to express data restrictions that extend beyond basic types. Strings, for example, support contraints such as:

These constraints will be used to validate incoming GraphQL mutations and violations will return descriptive error messages.

For example, here is a sample of JSON schema defining a "Shipment" entity with a "created" field that must adhere to a "date-time" format”:

"Shipment": {
"description": "Shipment information",
"type": "array",
"items": {
"type": "object",
"properties": {
"created": {
"type": "string",
"format": "date-time" <---- Values for this field must adhere to "date-time" format!
},

The following GraphQL mutation attempts to add a new shipment object with a malformed value for the "created" field:

mutation addShipment {
add_Shipment(input: { created: "yesterday" }) {
result {
_id
created
}
}
}

The following GraphQL error will be returned describing the violation:

'yesterday' is not a 'date-time'
Failed validating 'format' in schema['properties']['Shipment']['items']['properties']['created']:
{'format': 'date-time',
'type': 'string'}
On instance['Shipment'][0]['created']:
'yesterday'

You can learn more about the string constraints mentioned above here - Vendia Share currently supports all constraints available in JSON schema draft 7.

Indexes

Indexes can be defined in the JSON schema to support efficient queries on arbitrary attributes via the GraphQL filter argument. Indexes can also be used to sort the results of list queries via the GraphQL order argument.

Indexes can only be added to entities of type "array" and are restricted to top-level scalar fields (e.g. “string”, “number”, “integer”) on these entities.

The top-level directive "x-vendia-indexes" is used to define indexes on attributes. For example, to support an efficient list query of Orders by owner, an index can be defined in the schema referencing the owner property of the existing Order type.

{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://vendia.com/schemas/sample.schema.json",
"title": "Acme Uni Schema",
"description": "Defines data model for Acme's Product Uni",
"x-vendia-indexes": {
"OrderOwnerIndex": [
{
"type": "Order",
"property": "owner"
}
]
}
...
}

Filtering on an indexed field

Using the index defined above we can now list Orders, filtering on the owner property, and make use of our new index:

list_OrderItems(filter: {owner: {eq: "bob@acme.com"}}) {
nextToken
_OrderItems {
_id
owner
orderContent
}
}

Indexes can be used with the following GraphQL filter operators:

  • eq
  • gt
  • lt
  • le
  • ge
  • between
  • beginsWith

The remaining filter operators can be used, but your list query will no longer take advatage of the index.

Sorting list results by an indexed field

In addition to filtering, list query results can now be sorted by the owner property in ascending or descending order:

list_OrderItems(order: { owner: DESC }) {
nextToken
_OrderItems {
_id
owner
orderContent
}
}

Note that the filter and order arguments can be used in the same query with the following restrictions:

  1. Only one index can be used per query - if order and filter make use of two different indexes, the index used for order will take precedence.
  2. If order and filter are both used with the same index, then filter must be restricted to the supported operators listed above.

Vendia-specific JSON schema restrictions

Vendia Share endeavors to support the widest possible range of data models allowable in standard JSON schema. That said, translating JSON schema into strongly-typed GraphQL APIs requires us to enforce some minor restrictions as GraphQL itself is simply more strict about what can and cannot be supported.

While you may never bump into the following restrictions, they are listed here for clarity and transparency. If your JSON schema happens to violate any of the following rules, you will receive an error message explaining the problem and can update your schema accordingly.

  1. ”Empty” objects aren’t allowed in GraphQL schema. All "object" types must have "properties" and "properties", in turn, must contain at least one property definition.
  2. Similarly, all "array" types must contain an "items" definition.
  3. Any fields marked as required via the “required” array property must be defined on the corresponding "object" definition.
  4. JSON schema uses "additionalProperties" to determine whether an "object" type can include additional properties not defined explicitly in your JSON schema. The value of this property will always be set to false implicitly by Vendia Share. GraphQL is strongly-typed and does not allow storing/retrieving arbitrary additional fields on objects.
  5. JSON schema “combining” functionality (e.g. allOf, anyOf, oneOf, not) is not supported at this time.
  6. JSON schema definitions must use the "definitions" keyword rather than "$defs" (consistent with JSON schema draft 7).

Limits

A Uni is limited to a total of 12 indexes. Indexes can be defined at Uni registration time or added later via schema evolution. Only one index change is allowed per schema evolution. Learn more about Schema evolution and adding/removing indexes here.