GraphQL and General-Purpose Indexer (Beta)
This content describes an alpha/beta feature or service. These early stage features and services are in active development, so details are likely to change.
This feature or service is currently available in
- Devnet
- Testnet
- Mainnet
The GraphQL and sui-indexer-alt (Indexer) stack are part of the Sui data access infrastructure. The stack provides access to on-chain data through a high-performance GraphQL service backed by a scalable and general-purpose indexer. This stack is optimized for developers who need flexible queries, structured output, and historical access to data (with configurable retention) across the Sui network.
GraphQL is ideal for applications that require rich query patterns over structured data, such as fetching owned objects, transaction history, specific onchain attributes, and more. The GraphQL service runs on top of a Postgres-compatible database that is updated by different Indexer pipelines in parallel.
The General-purpose Indexer includes configurable checkpoint-based pipelines that extract data from the Sui remote checkpoint store and full nodes. The pipelines write processed data to a database optimized for GraphQL query access.
Together, the GraphQL service and Indexer offer a modular and production-ready data stack for builders, wallet developers, explorers, and indexer/data providers.
JSON-RPC is deprecated. Migrate to either gRPC or GraphQL RPC by April 2026.
Refer to the list of RPC or data providers that have enabled gRPC on their full nodes or offer GraphQL RPC. Contact a provider directly to request access. If your RPC or data provider doesnβt yet support these data access methods, ask them to enable support or contact the Sui Foundation team on Discord, Telegram, or Slack for help.
Refer to Access Sui Data for an overview of options to access Sui network data.
The GraphQL RPC release stage is currently in beta. Refer to the high-level timeline for releases.
The key components of the GraphQL and General-purpose Indexer stack include the following:
-
General-purpose Indexer: Ingests and transforms Sui checkpoint data using configurable and parallel pipelines, then writes it into a Postgres-compatible database. It can be configured to use the Sui remote checkpoint store and a full node as its sources.
-
Postgres-compatible database: Stores indexed data for GraphQL queries. It is tested using GCP AlloyDB, but you can run any Postgres-compatible database. Test alternative databases and share feedback on performance, cost, and operational characteristics.
-
GraphQL service: Serves structured queries over indexed data. It follows the GraphQL specification and the supported schema is documented in the GraphQL API reference. Refer to the GraphQL getting started guide.
-
Archival Service: Enables point lookups for historical data from a key-value store. If unavailable, the GraphQL service falls back to the Postgres-compatible database for lookups, which might be limited by that database's retention policy. See Archival Store and Service for more information.
-
Consistent Store: Answers queries about the latest state of the network within the last hour (objects owned by addresses, objects by type, balances by address and type). Consistency is guaranteed by pinning queries to a specific (recent) checkpoint.
-
Full node: Enables transaction execution and simulation.
When to use GraphQLβ
Use GraphQL if your application:
-
Requires historical data with configurable retention or filtered access to data, such as all transactions sent by an address.
-
Needs to display structured results in a frontend, such as wallets and dashboards.
-
Benefits from flexible, composable queries that reduce overfetching.
-
Relies on multiple data entities, such as transactions, objects, or events, in a single request, or in a consistent fashion when spread over multiple requests as if the responses came from a snapshot at some checkpoint.
Deployment optionsβ
You can run or use the GraphQL and Indexer data stack in the following configurations:
Fully managed serviceβ
As a developer, you can access GraphQL as a service from an indexer operator or data provider who runs and operates the full stack behind the scenes. Reach out to your data provider and ask if they already offer or plan to offer this service.
Partial self-managedβ
As a developer, you can:
-
Run the Indexer pipelines and GraphQL service, while using the Archival Service and a full node from an RPC provider or indexer operator.
-
Configure and manage a Postgres-compatible database (local Postgres, AlloyDB, and so on) as the primary data store.
-
Deploy the self-managed components on cloud infrastructure or baremetal.
Fully self-managedβ
As a developer, indexer operator, or RPC provider, you can:
-
Run the complete stack: Indexer pipelines, GraphQL service, Postgres-compatible database, Archival Service, Consistent Store and full node on cloud infrastructure or bare metal.
-
Serve GraphQL to your own applications or to other builders and third-party services.
Refer to For RPC providers and data operators for relevant information.
Working with the GraphQL serviceβ
The GraphQL service exposes a query surface conforming to GraphQL concepts. It allows pagination, filtering, and consistent snapshot queries. The service also supports runtime configuration for schema, query cost limits, and logging.
The GraphQL schema is defined in the GraphQL reference. You can explore supported types and fields there, use the GraphiQL IDE to test queries, and read documentation on the up-to-date schema.
The GraphQL service is deployed as a single binary implementing a stateless, horizontally scalable service. Queries are served with data from one or more of a Postgres-compatible database (filters over historical data), Archival Service (point lookups), Consistent Store (live data), or full node (execution and simulation), based on need. Access to these stores must be configured with the service on start-up, otherwise the service might fail to respond correctly to requests. More details on how to set up, configure, and run the service is available in its README.
Requests to GraphQL are subject to various limits, to ensure resources are shared fairly between clients. Each limit is configurable, and the values configured for an instance can be queried through Query.serviceConfig. Requests that do not meet limits return with an error. The following limits are in effect:
-
Request size: Requests might not exceed a certain size in bytes. The limit is spread across a transaction payload limit, which applies to all values and variable bindings that are parameters to transaction signing, execution, and simulation fields (default: 175KB), and a query payload limit which applies to all other parts of the query (default: 5KB).
-
Request timeout: Time spent on each request is bounded, with different bounds for execution (default: 74s) and regular reads (default: 40s).
-
Query input nodes and depth: The query cannot be too complex, meaning it cannot contain too many input nodes or field names (default: 300) or be too deeply nested (default: 20).
-
Output nodes: The service estimates the maximum number of output nodes the query might produce, assuming every requested field is present, every paginated field returns full pages, and every multi-get finds all requested keys. This estimate must be bounded (default: 1,000,000).
-
Page and multi-get size: Each paginated field (default: 50) and multi-get (default: 200) is subject to a maximum size. Certain paginated fields might override this to provide a higher or lower maximum.
-
(TBD) Rich queries: A request can contain only a bounded number (default: 5) of queries that require dedicated access to the database (cannot be grouped with other requests).
Working with General-purpose Indexerβ
General-purpose indexer fetches checkpoints data from either a remote object store, local files, or a full node RPC, and indexes data into multiple database tables through a set of specialized pipelines. Each pipeline is responsible for extracting specific data and writing to its target tables.
Full list of tables and their schemas
// @generated automatically by Diesel CLI.
diesel::table! {
coin_balance_buckets (object_id, cp_sequence_number) {
object_id -> Bytea,
cp_sequence_number -> Int8,
owner_kind -> Nullable<Int2>,
owner_id -> Nullable<Bytea>,
coin_type -> Nullable<Bytea>,
coin_balance_bucket -> Nullable<Int2>,
}
}
diesel::table! {
coin_balance_buckets_deletion_reference (cp_sequence_number, object_id) {
object_id -> Bytea,
cp_sequence_number -> Int8,
}
}
diesel::table! {
cp_sequence_numbers (cp_sequence_number) {
cp_sequence_number -> Int8,
tx_lo -> Int8,
epoch -> Int8,
}
}
diesel::table! {
ev_emit_mod (package, module, tx_sequence_number) {
package -> Bytea,
module -> Text,
tx_sequence_number -> Int8,
sender -> Bytea,
}
}
diesel::table! {
ev_struct_inst (package, module, name, instantiation, tx_sequence_number) {
package -> Bytea,
module -> Text,
name -> Text,
instantiation -> Bytea,
tx_sequence_number -> Int8,
sender -> Bytea,
}
}
diesel::table! {
kv_checkpoints (sequence_number) {
sequence_number -> Int8,
checkpoint_contents -> Bytea,
checkpoint_summary -> Bytea,
validator_signatures -> Bytea,
}
}
diesel::table! {
kv_epoch_ends (epoch) {
epoch -> Int8,
cp_hi -> Int8,
tx_hi -> Int8,
end_timestamp_ms -> Int8,
safe_mode -> Bool,
total_stake -> Nullable<Int8>,
storage_fund_balance -> Nullable<Int8>,
storage_fund_reinvestment -> Nullable<Int8>,
storage_charge -> Nullable<Int8>,
storage_rebate -> Nullable<Int8>,
stake_subsidy_amount -> Nullable<Int8>,
total_gas_fees -> Nullable<Int8>,
total_stake_rewards_distributed -> Nullable<Int8>,
leftover_storage_fund_inflow -> Nullable<Int8>,
epoch_commitments -> Bytea,
}
}
diesel::table! {
kv_epoch_starts (epoch) {
epoch -> Int8,
protocol_version -> Int8,
cp_lo -> Int8,
start_timestamp_ms -> Int8,
reference_gas_price -> Int8,
system_state -> Bytea,
}
}
diesel::table! {
kv_feature_flags (protocol_version, flag_name) {
protocol_version -> Int8,
flag_name -> Text,
flag_value -> Bool,
}
}
diesel::table! {
kv_genesis (genesis_digest) {
genesis_digest -> Bytea,
initial_protocol_version -> Int8,
}
}
diesel::table! {
kv_objects (object_id, object_version) {
object_id -> Bytea,
object_version -> Int8,
serialized_object -> Nullable<Bytea>,
}
}
diesel::table! {
kv_packages (package_id, package_version) {
package_id -> Bytea,
package_version -> Int8,
original_id -> Bytea,
is_system_package -> Bool,
serialized_object -> Bytea,
cp_sequence_number -> Int8,
}
}
diesel::table! {
kv_protocol_configs (protocol_version, config_name) {
protocol_version -> Int8,
config_name -> Text,
config_value -> Nullable<Text>,
}
}
diesel::table! {
kv_transactions (tx_digest) {
tx_digest -> Bytea,
cp_sequence_number -> Int8,
timestamp_ms -> Int8,
raw_transaction -> Bytea,
raw_effects -> Bytea,
events -> Bytea,
user_signatures -> Bytea,
}
}
diesel::table! {
obj_info (object_id, cp_sequence_number) {
object_id -> Bytea,
cp_sequence_number -> Int8,
owner_kind -> Nullable<Int2>,
owner_id -> Nullable<Bytea>,
package -> Nullable<Bytea>,
module -> Nullable<Text>,
name -> Nullable<Text>,
instantiation -> Nullable<Bytea>,
}
}
diesel::table! {
obj_info_deletion_reference (cp_sequence_number, object_id) {
object_id -> Bytea,
cp_sequence_number -> Int8,
}
}
diesel::table! {
obj_versions (object_id, object_version) {
object_id -> Bytea,
object_version -> Int8,
object_digest -> Nullable<Bytea>,
cp_sequence_number -> Int8,
}
}
diesel::table! {
sum_displays (object_type) {
object_type -> Bytea,
display_id -> Bytea,
display_version -> Int2,
display -> Bytea,
}
}
diesel::table! {
tx_affected_addresses (affected, tx_sequence_number) {
affected -> Bytea,
tx_sequence_number -> Int8,
sender -> Bytea,
}
}
diesel::table! {
tx_affected_objects (affected, tx_sequence_number) {
tx_sequence_number -> Int8,
affected -> Bytea,
sender -> Bytea,
}
}
diesel::table! {
tx_balance_changes (tx_sequence_number) {
tx_sequence_number -> Int8,
balance_changes -> Bytea,
}
}
diesel::table! {
tx_calls (package, module, function, tx_sequence_number) {
package -> Bytea,
module -> Text,
function -> Text,
tx_sequence_number -> Int8,
sender -> Bytea,
}
}
diesel::table! {
tx_digests (tx_sequence_number) {
tx_sequence_number -> Int8,
tx_digest -> Bytea,
}
}
diesel::table! {
tx_kinds (tx_kind, tx_sequence_number) {
tx_kind -> Int2,
tx_sequence_number -> Int8,
}
}
diesel::table! {
watermarks (pipeline) {
pipeline -> Text,
epoch_hi_inclusive -> Int8,
checkpoint_hi_inclusive -> Int8,
tx_hi -> Int8,
timestamp_ms_hi_inclusive -> Int8,
reader_lo -> Int8,
pruner_timestamp -> Timestamp,
pruner_hi -> Int8,
}
}
diesel::allow_tables_to_appear_in_same_query!(
coin_balance_buckets,
coin_balance_buckets_deletion_reference,
cp_sequence_numbers,
ev_emit_mod,
ev_struct_inst,
kv_checkpoints,
kv_epoch_ends,
kv_epoch_starts,
kv_feature_flags,
kv_genesis,
kv_objects,
kv_packages,
kv_protocol_configs,
kv_transactions,
obj_info,
obj_info_deletion_reference,
obj_versions,
sum_displays,
tx_affected_addresses,
tx_affected_objects,
tx_balance_changes,
tx_calls,
tx_digests,
tx_kinds,
watermarks,
);
Below are brief descriptions of the various categories of pipelines based on the type of data they handle: