GraphQL RPC
The GraphQL RPC service is currently under development. The MVP is scheduled to be launched at the end of January 2024.
This document explains some of the common concepts when working with GraphQL such as pagination, fragments, or variables. For an introduction to GraphQL, see GitHub's Introduction to GraphQL.
If you are interested in how to interact with the Sui network via the GraphQL RPC, see Getting Started.
Discovering the schema
GraphQL offers an introspection feature that allows you to query for the schema that is loaded by a service instance. The official documentation provides an overview on introspection.
Headers
The service accepts the following optional headers:
x-sui-rpc-version
to specify which RPC version to use (note that this is currently unavailable)x-sui-rpc-show-usage
will return the response with extra query complexity information.
By default, each request will return the service's version in the response header: x-sui-rpc-version
.
curl -v -X POST https://graphql-beta.mainnet.sui.io \
--header 'x-sui-rpc-show-usage: true' \
--header 'Content-Type: application/json' \
--data '{
"query": "query { epoch { referenceGasPrice } }"
}'
The response for the request above will look similarly to the following:
// omitted for brevity
x-sui-rpc-version: 0.1.0
{
"data": {
"epoch": {
"referenceGasPrice": "750"
}
},
"extensions": {
"usage": {
"nodes": 2,
"depth": 2,
"variables": 0,
"fragments": 0,
"query_payload": 37
}
}
}
Working with variables
Variables are a powerful way for passing different object IDs, Digests, Sui addresses, and other kinds of data for full flexibility. The following is an example for querying the reference gas price for epoch 100
, by passing the epoch as a variable.
When working with the online GraphQL IDE, paste the variables' values in the Variables
pane on the bottom left of the window, without the variables
keyword. When not using variables, these need to be removed from the query.
A variable is declared and referred to using the $
symbol and its type (in this example Int
) must be specified. Note that variables must be declared in the root query
.
query ($epochID: Int) {
epoch(id: $epochID) {
referenceGasPrice
}
}
where the variable is:
{
"epochID": 100
}
Multiple data in one query
The previous RPC service required multiple queries for more complex data retrieval. Now, with GraphQL, you can specify the data you need in one single query.
For example, the following query retrieves the first 20 transaction blocks (along with the digest, the sender's address, the gas object used to pay for the transaction, the gas price, and the gas budget) after a specific transaction block at epoch 97
. In the previous RPC, this would have required multiple API calls.
# Fetch the first 10 transactions for epoch 97
query {
epoch(id:97) {
transactionBlocks(first: 10) {
pageInfo {
hasNextPage
endCursor
}
edges {
cursor
node {
digest
sender {
address
}
effects {
gasEffects {
gasObject {
address
}
}
}
gasInput {
gasPrice
gasBudget
}
}
}
}
}
}
By default, the number of results is limited to 50 items. To learn more about how GraphQL works with pagination, check out the official documentation and our following section on pagination.
Pagination
Sui GraphQL RPC limits the number of items that are returned in a request. The default page limit size is set to 50 items. If there are more items requested than the max default page limit, use cursors
to navigate between pages. A cursor refers to a specific position in the dataset. To access the start and end cursors of a page, use the pageInfo
field that exposes the cursor data and two additional fields indicating if there is a previous or next page. For example, if we want to get the checkpoints
data:
query {
checkpoints {
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
The query's result is:
{
"data": {
"checkpoints": {
"pageInfo": {
"hasPreviousPage": false,
"hasNextPage": true,
"startCursor": "MA",
"endCursor": "NA"
}
}
}
}
The pageInfo.startCursor
and pageInfo.endCursor
indicate the index of the first and last item in the response. You can use pageInfo.hasNextPage
to determine if there is a next page, and then use the endCursor
's value and pass it to the after
filter in the connection to traverse the next set of elements:
query {
checkpoints(after: "MA") {
nodes {
digest
}
}
}
When using pagination filters, you can only specify first
or last
, but not both.
Fragments
Fragments are reusable units that can be included in the queries as needed. The official docs contain more information about fragments. The following is an example of how fragments are used for querying a dynamic field of an owner.
query DynamicField {
object(
address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
) {
dynamicField(
name: {
type: "0x2::kiosk::Listing",
bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1VhfwA",
}
) {
...DynamicFieldSelect
}
}
}
fragment DynamicFieldSelect on DynamicField {
name {
...DynamicFieldNameSelection
}
value {
...DynamicFieldValueSelection
}
}
fragment DynamicFieldNameSelection on MoveValue {
type {
repr
}
data
bcs
}
fragment DynamicFieldValueSelection on DynamicFieldValue {
... on MoveValue {
type {
repr
}
data
__typename
}
... on MoveObject {
hasPublicTransfer
contents {
type {
repr
}
data
}
__typename
}
}
Migrating guides
The following sections highlight the steps necessary for you to take to migrate your codebase to leverage the Sui GraphQL RPC.
From JSON RPC and examples
The former RPC API provided several individual endpoints to get specific data (for example, get_totalTransactionBlocks
). While this can be achieved similarly with GraphQL, it is more powerful to determine the data you need in one query that involves multiple aspects (for example, transactions, balance, coins), execute that query, and get the results. This guide showcases how to (naively) use GraphQL instead of JSON RPC on a few original endpoints.
While a one-on-one mapping from JSON RPC to GraphQL is most likely possible, we recommend avoiding that route and to, instead, model your queries to leverage the full power of GraphQL.
Example 1: Get total transaction blocks
The goal is to get the total number of transaction blocks in the network.
- JSON-RPC
- GraphQL
{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getTotalTransactionBlocks",
"params": []
}
query {
checkpoint {
networkTotalTransactions
}
}
Example 2: Get a specific transaction block
The goal is to get the transaction block by its digest.
- JSON-RPC
- GraphQL
{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getTransactionBlock",
"params": [
"Hay2tj3GcDYcE3AMHrej5WDsHGPVAYsegcubixLUvXUF",
{
"showInput": true,
"showRawInput": false,
"showEffects": true,
"showEvents": true,
"showObjectChanges": false,
"showBalanceChanges": false
}
]
}
query {
transactionBlock(digest: "Hay2tj3GcDYcE3AMHrej5WDsHGPVAYsegcubixLUvXUF") {
gasInput {
gasSponsor {
address
}
gasPrice
gasBudget
}
effects {
status
timestamp
checkpoint {
sequenceNumber
}
epoch {
epochId
referenceGasPrice
}
}
}
}
Example 3: Get coin objects owned by an address
The goal is to return all Coin<0x2::sui::SUI>
objects an address owns.
- JSON-RPC
- GraphQL
query {
"jsonrpc": "2.0",
"id": 1,
"method": "suix_getCoins",
"params": [
"0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9", //owner
"0x2::sui::SUI", //coin type
"0xe5c651321915b06c81838c2e370109b554a448a78d3a56220f798398dde66eab", //cursor
3 //limit
]
}
query {
address(address: "0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9") {
coins(
first: 3,
after: "IAB3ha2PEA4ESRF4UErsJufJEwYpmSbCq7UNpxIHnLhG",
type: "0x2::sui::SUI"
) {
nodes {
address
}
}
}
}
The cursor is now passed in the after
(or before
) fields on the connection, and the limit in the first
or last
fields.