Using the GraphQL API

Each Node in a Uni hosts a GraphQL API that provides full CRUD support for your data in addition to extended functionality such as working with Files and Smart Contracts.

Queries

Generated Queries

Vendia generates the following GraphQL queries for the top-level entities in your data model.

get_X

Supports retrieving an item by id. An optional version number parameter can be provided to query for the state of the item at a particular version.

list_XItems

Supports listing items. List results can be filtered using the filter parameter. For data models with indexes defined, list filters will automatically use the first matching index found. Returns a nextToken that can be used for pagination.

list_XVersions

List the versions of a particular object. The returned result contains the ordinal version numbers as well as block and transaction metadata. The version number can be used in a get query to retrieve the object at a particular version. Returns a nextToken that can be used for pagination.

type Vendia_Version {
    ordinal: Int!
    block: String!
    transactions: [Vendia_Version_Transaction]
}

Read Modes for Queries

Vendia supports read modes to specify the level of consistency required for query operations, defined by the optional readMode parameter. Each read mode has different performance and consistency tradeoffs. To understand the impact of each read mode it helps to understand the differences between the world state and the ledger within your Node(s).

Type of ConsistencyDirty ReadsOperation LatencyConsistency Guarantees
CACHEDLikelyFastestTransactions are written to a local cache on a given Node and can be out of date up to cache timeout.
NODE_COMMITTEDPossibleFastTransactions are written to the world state on a given Node but, perhaps, not yet to the ledger.
NODE_LEDGEREDNoSlowTransactions are written to the world state and ledger on a given Node.
UNI_LEDGEREDNoSlowestTransactions are written to the world state and ledger on all Nodes.

What are "dirty reads"?

In Vendia, blocks in the ledger can contain any number of transactions. A "dirty read" is a query that returns data that has been modified in the latest block that has yet to have all transactions applied (a block with status APPLYING). When blocks are fully applied they are marked with status NODE_LEDGERED and then with UNI_LEDGERED when the block has been applied across the Uni.

Note that in practice returning data from partially applied blocks may not be a concern for many use-cases since individual mutations are committed atomically. However, if dirty reads are a concern, strong read modes can be used to guarantee that reads occur from fully applied blocks.

CACHED

Returns a potentially cached version of the data, including pending transactions from the write-ahead-log. The returned data may be out of date for up to a minute and may not be linearized across multiple requests from the same client.

This read mode offers the best performance but the weakest consistency of all the read modes.

NODE_COMMITTED (default)

Returns world state from the local Node including pending transactions from the write-ahead-log. The returned data may not yet have been applied on other Nodes and may include "dirty reads" of partially applied blocks.

This is the best option for interactive applications where fast read-after-write latency is required.

NODE_LEDGERED

Returns world state from the local Node, guaranteeing that all returned data has been fully applied and ledgered in a block on the local Node. The data may not yet have been applied or ledgered on other Nodes. This does not allow "dirty reads" of partially applied blocks.

UNI_LEDGERED

Returns world state from the local Node, guaranteeing that all returned data has been fully applied and ledgered in a block on all Nodes. This does not allow "dirty reads" of partially applied blocks.

Query Example Using Read Modes

Let’s assume you prefer to optimize for minimum response latency over data consistency. In this case, you can use the CACHED read mode by adding the readMode parameter to your query.

query getShapeQuery {
  get_Shape(id: "0180f1d5-7b0c-c03a-dd22-54eb75b807ef", readMode: CACHED) {
    color
    name
    num_sides
  }
}

Mutations

GraphQL mutations allow clients to modify objects defined in the data model. All mutations modify the state of an object, store the change in the global ledger, and store a new version record for the object.

Synchronous mutations return a transaction result object that contains both transaction metadata and the contents of the updated object:

type Self_MyObject_Transaction_Result_ {
    transaction: Vendia_Transaction!
    result: Self_MyObject
}
type Vendia_Transaction {
    _id: String!  # id of the modified object
    _owner: String!
    transactionId: String!
    version: String!
    submissionTime: String!
}

For example, the following mutation:

add_Shape(
   input: { 
       name: "square", 
       numSides: 4
   }
) { 
   transaction { 
       transactionId 
   }
   result {
       _id 
       name 
       numSides 
   } 
 }

returns the result:

{
    "transaction": {
        "transactionId": "f54160b5-58f1-485c-9745-737e539eb262"
    },
    "result": {
        "_id": "234160b5-58f1-485c-9745-737e539eb20f"
        "name": "square",
        "numSides": 4
    }
}

Note: Mutations return a transactionId that can be used for status polling and correlation purposes. To configure failure notifications for mutations, see Dead-letter notifications.

Generated Mutations

Vendia generates the following GraphQL mutations for the top-level entities in your data model.

add_X / remove_X

Supports adding and removing items for array types.

create_X / delete

Supports creating and deleting items for object types.

put_X

Put mutations update an item by replacing the entire item with the input specified in the mutation. All required fields must be specified in the input, and any absent fields will be removed in the datastore.

i.e.

put_Shape(
   id: "234160b5-58f1-485c-9745-737e539eb20f",
   input: { 
       name: "square", 
       color: "red", 
       numSides: 4 
   }
) { transaction { transactionId } }

update_X

Supports partial update of an item. Only the fields specified in the input will be updated. Fields absent in the input are preserved in the datastore. Fields can be explicitly removed by specifying a null value.

i.e.

update_Shape(
   id: "234160b5-58f1-485c-9745-737e539eb20f",
   input: { 
       name: null, 
       numSides: 5
   }
) { 
   transaction { _id transactionId }
   result { name numSides } 
 }

This mutation removes the name field, preserves the existing value of the color field, and updates the numSides field.

Note: Nested objects can be explicitly removed if they do not contain required fields. i.e.

update_MyObject(
   id: "234160b5-58f1-485c-9745-737e539eb20f",
   input: { 
       nestedObject: null
   }
) {
   transaction { _id transactionId }
   result { name numSides } 
}

Sync Modes for Mutations

Vendia supports multiple synchronization modes for mutation operations, specified by the optional syncMode parameter. Sync Modes define when the mutation operation returns the result to the client. Each sync mode has different performance and consistency tradeoffs.

Type of ConsistencyDirty ReadsOperation LatencyConsistency Guarantees
NODE_COMMITTEDPossibleFastTransactions are written to the world state on a given Node but, perhaps, not yet to the ledger. Writes are guaranteed to be read on the local node if using NODE_COMMITTED read mode.
NODE_LEDGEREDNoSlowTransactions are written to the world state and ledger on a given Node.
UNI_LEDGEREDNoSlowestTransactions are written to the world state and ledger on all Nodes.
ASYNCN/AFastestTransactions are queued up and a transaction ID is returned that can be polled. Transactions can be queried once they are written to the world state and the ledger on a given Node.

ASYNC

Queues the transaction for asynchronous processing and returns immediately. The transaction is guaranteed to either eventually be committed, ledgered, and replicated to all Nodes or be sent to the dead-letter queue. With ASYNC, the result property in the response will always be empty. Clients can optionally configure GraphQL subscriptions or block notifications to receive notification when transactions are committed.

This mode has the best performance profile and is the best option for batch data loading.

NODE_COMMITTED (default)

This synchronous mode returns the result once the transaction has been committed to the write-ahead-log on the local Node and queued for asynchronous processing. Subsequent queries using NODE_COMMITTED read mode are guaranteed to read modifications from the write-ahead-log. The transaction is guaranteed to be eventually ledgered and replicated to all the Nodes in the Uni.

This option provides the best read-after-write latency and is well suited for interactive applications.

NODE_LEDGERED

This synchronous mode returns the result once the transaction has been committed and ledgered on the local Node. The transaction is guaranteed to be eventually replicated to all the Nodes in the Uni.

For LEDGERED sync modes, the result property contains the version of the object following the submitted transaction. The returned result is read-isolated against other concurrent writes to the object. To retrieve the latest version of the object, you can use a get query without the version number specified.

UNI_LEDGERED

This synchronous mode returns the result once the transaction has been committed and ledgered on all Nodes in the Uni.

Synchronous Mutation Example

Let’s assume you need to ensure that the data is guaranteed to be replicated to all the Nodes in the Uni. In this case, you will likely use the UNI_LEDGERED consistency mode. To select the consistency mode, you add the syncMode parameter to your mutation.

mutation addShapeMutation {
  add_Shape(
    input: {color: "blue", name: "hexagon", num_sides: 6}
    syncMode: UNI_LEDGERED
  ) {
      result {
          _id
      }
    }
}

Sync Mode error states

Sync mutation timeouts

The Vendia GraphQL API has a hard limit of 30 seconds per request. During standard operation queries and mutations return well within this 30 second window. An exception to this general rule is certain types of synchronous mutations.

The API will quicky respond when a synchronous mutation uses a sync mode of NODE_COMMITTED. This is because the Node is updating a local copy of the data and then submitting the mutation to the consensus queue. In the case of NODE_LEDGERED / UNI_LEDGERED, the does all of the above and then waits for the other Nodes to reach consensus on the result of the mutation. Reaching consensus on a newly created transaction takes additional time because of the need to work across geographically distributed Nodes.

If there is a large consensus queue backlog or a Node receives a large number of transactions in a small period of time, it may take longer than 30 seconds to process all the existing the transactions and gain consensus.

The result of a timed out sync mutation with sync mode of NODE_LEDGERED or UNI_LEDGERED is as follows transaction timed out after 24000ms: ['0180dbbc-2be5-377b-e6f7-90cd014bdce9']).

Sync mutations submitted with NODE_COMMITTED, do not have to wait for consensus and therefore will not timeout in this way.

Even though there is a timeout message returned to the client, the Node will continue to process the submitted transaction. A transaction successfully reaching the consensus queue is never lost unless the region in which the Node operates is lost as well. Once the transaction gets to the front of the consensus queue, it will still be processed and ledgered across all Nodes.

Paused Consensus

When there is a non-recoverable issue within consensus the Uni stops processing the mutation queue to ensure data correctness. This non-recoverable issue could be due to a network outage or issues within a cloud service provider. If this happens Vendia is immediately notified and will attempt to find and fix the problem. When consensus is paused your Uni will be transitioned into an error state, as captured below.

Whilst in this error state you will still be able to query your Uni and Node, and submit sync and async mutations. NODE_COMMITTED transactions will continue as usual, though due to consensus being paused NODE_LEDGERED / UNI_LEDGERED sync mutations will always timeout. Once the Uni is no longer in an error state, it will catch up on the backlog of transactions and, once that occurs, NODE_LEDGERED and UNI_LEGERED mutations will return to normal.

Next Steps

Using File Storage

Integrating a Vendia chain with other Cloud, Web, and Mobile Services

Learning More

Terms and Definitions