Skip to main content

GraphQL

The iota-graphql-rpc binary serves a GraphQL endpoint backed by the IOTA Indexer database. See the Extended Data Services overview for how it fits together with the indexer and the JSON-RPC service.

Scope

The GraphQL service answers GraphQL queries by reading data from the IOTA Indexer database. It exposes reads of checkpoints, transactions, events, objects (including past versions within the retained window), Move packages and types, balances, dynamic fields, and the on-chain name registry. GraphQL is ideal for applications that require rich query patterns over structured data.

Two operations are not served from the database: executeTransactionBlock and dryRunTransactionBlock. Both are forwarded to a fullnode over gRPC.

The GraphQL service holds no state of its own. Multiple instances can run in parallel against the same database.

Dependencies

  • An indexer Postgres database. The schema version in the database must match the one compiled into the GraphQL binary; otherwise the GraphQL service refuses to start. For the executeTransactionBlock operation to work properly the indexer writer should be running and synced with the tip of the network. See IOTA Indexer for details on running the indexer writer.
  • A fullnode gRPC endpoint. Required at start-up — the server fails to start if --node-rpc-url is not set, even when no client issues a mutation or a dry run.
  • An optional REST KV store for historic fallback. When configured, it lets the GraphQL service serve data already pruned by the indexer writer. Currently experimental. See Pruned data below and Pruning for the indexer-side retention model.

Hardware requirements

  • CPU: 2 cores per instance
  • Memory: 4 GB per instance

Scale up the number of instances when client request load grows.

Running the GraphQL service

A minimal start command:

iota-graphql-rpc start-server \
--db-url postgres://<user>:<password>@<host>:5432/<db> \
--node-rpc-url http://<fullnode>:<grpc-port> \
--config service.toml

Other subcommands:

  • iota-graphql-rpc generate-config <PATH> writes a fully-populated TOML with the current defaults.
  • iota-graphql-rpc generate-schema --file <PATH> writes the active GraphQL schema in SDL format. Useful when generating client code.

Configuration

Settings come from two places:

  • CLI flags on the start-server subcommand.
  • A TOML file passed with --config <PATH>. If no file is passed, the defaults apply.

A starting TOML with every field set to its default can be generated with:

iota-graphql-rpc generate-config <PATH>

The most important settings are covered in subsections below. All settings are listed in the reference table at the bottom of the page.

Database connection

--db-url sets the Postgres connection URL (e.g. postgres://<user>:<password>@<host>:5432/<db>); see the Postgres docs for the full URI syntax. --db-pool-size sets the connection pool size — raise it for higher concurrency, but stay within the Postgres max_connections setting and account for other clients of the same database.

Fullnode endpoint

--node-rpc-url is the gRPC URL of a fullnode that the GraphQL service forwards executeTransactionBlock and dryRunTransactionBlock to. It is required at start-up.

Pruned data

Experimental

The historic fallback depends on the API from the iota-rest-kv crate which is still being finalised. Treat the flags below as experimental — they may change or be replaced.

Data already pruned by the indexer writer can be served by the GraphQL service using a historic fallback feature. To use it, the operator runs a separate self-hosted REST KV store and points the GraphQL service at it via --fallback-kv-url. When --fallback-kv-url is unset (the default), no fallback is performed and pruned data is unreachable via GraphQL.

These queries fall back to the KV store when their data has been pruned from the database:

  • checkpoint, checkpoints, Epoch.checkpoints
  • transactionBlock, transactionBlocksByDigests
  • transactionBlocks with filter atCheckpoint, affectedAddress, or transactionIds, and the matching Checkpoint.transactionBlocks and Address.transactionBlocks (AFFECTED relation)
  • events with filter transactionDigest
  • object at a past version, including dynamic fields resolved at a parent object's version
note

Resolving a dynamic field or dynamic object field at a parent object's version needs that version recorded in the indexer's objects_version table. On indexers restored from a snapshot, versions below the restore point cannot be served this way and return DATA_PRUNED.

The --fallback-kv-multi-fetch-batch-size, --fallback-kv-concurrent-fetches, and --fallback-kv-cache-size knobs tune the KV client's request batching, concurrency, and in-memory cache.

HTTP server

--host and --port set the address the GraphQL endpoint binds to (default 127.0.0.1:8000). The interactive GraphiQL IDE is served on the same address; its page title comes from --ide-title.

Prometheus metrics

The Prometheus endpoint exposes histograms and counters covering request rate, latency, errors, database pool utilisation, and watermark lag.

--prom-host and --prom-port set the address the Prometheus metrics are exposed on (default 0.0.0.0:9184).

Disabled features

Operators can disable selected groups of queries using the disabled-features TOML parameter. The available groups are:

NamePaths
coinsQuery.coinMetadata; Address.balance/balances/coins; Object.balance/balances/coins; Owner.balance/balances/coins
dynamic-fieldsObject.dynamicField/dynamicFields/dynamicObjectField; Owner.dynamicField/dynamicFields/dynamicObjectField
subscriptionsSubscription.events; Subscription.transactions
system-stateQuery.protocolConfig; Epoch.protocolConfigs/referenceGasPrice/validatorSet; SystemStateSummary.safeMode/storageFund/systemParameters/systemStateVersion

For instance, with coins disabled, the following query is rejected because Query.coinMetadata and Address.balance are both part of the coins group:

{
coinMetadata(coinType: "0x2::iota::IOTA") {
decimals
symbol
}
address(address: "0x...") {
balance(type: "0x2::iota::IOTA") {
totalBalance
}
}
}

Multiple values allowed, example:

disabled-features = ["subscriptions", "coins"]

Watermark task

The service maintains an in-process watermark that tracks the maximum checkpoint and epoch readable from the database. The background-tasks.watermark-update-ms TOML key sets how often the watermark is refreshed (default 500 ms). This controls how fresh data is served through GraphQL queries as data above the watermark is filtered out from the responses.

Max available range

A consistent view fixes the database state at a single checkpoint for the duration of a paginated query. Without it, paginating through objects could mix state from different checkpoints — page 2 might miss objects that were deleted after page 1 was returned, or include objects that were created in the meantime. GraphQL keeps a window of recent checkpoints over which it can reconstruct historical object state from the indexer's objects_backward_history table.

--max-available-range (also settable via the MAX_AVAILABLE_RANGE environment variable) sets the size of that window, in checkpoints. Larger values let pagination cursors on objects stay valid longer, at a cost that older cursors introduce more database load.

The value must not exceed the retention of the historical tables on the indexer side (objects_backward_history). If it does, GraphQL may return incorrect results for data already pruned by indexer writer.

Default value of 9000 allows pagination cursors on objects to stay alive for ≈ 30 minutes.

IOTA Names resolution

The [iota-names] TOML table configures the on-chain name registry the service resolves names against. The defaults match mainnet. Override the package address and registry IDs when running against a different network:

[iota-names]
package-address = "0x..."
object-id = "0x..."
payments-package-address = "0x..."
registry-id = "0x..."
reverse-registry-id = "0x..."

All settings

CLI flags

FlagDefaultDescription
--host127.0.0.1Host the GraphQL endpoint binds to.
--port, -p8000Port the GraphQL endpoint binds to.
--db-url, -dpostgres://postgres:postgrespw@localhost:5432/iota_indexerPostgres connection URL.
--db-pool-size10Number of Postgres connections held in the pool.
--prom-host0.0.0.0Host the Prometheus metrics endpoint binds to.
--prom-port9184Port the Prometheus metrics endpoint binds to.
--skip-migration-consistency-checkfalseSkip the start-up check that local migrations match the database.
--max-available-range9000Maximum window (in checkpoints) in which consistent view on objects can be served. Also MAX_AVAILABLE_RANGE env var.
--node-rpc-url(required)Fullnode gRPC URL used for executeTransactionBlock and dryRunTransactionBlock.
--fallback-kv-url(none)Experimental. REST KV store URL for historic fallback. Disabled when unset.
--fallback-kv-multi-fetch-batch-size100Experimental. Maximum number of keys per batch request to the fallback KV store. Also FALLBACK_KV_MULTI_FETCH_BATCH_SIZE env var.
--fallback-kv-concurrent-fetches10Experimental. Maximum number of concurrent batch requests to the fallback KV store. Also FALLBACK_KV_CONCURRENT_FETCHES env var.
--fallback-kv-cache-size100000Experimental. In-memory cache size for historic fallback. Also FALLBACK_KV_CACHE_SIZE env var.
--ide-title, -iIOTA GraphQL IDETitle displayed at the top of the GraphiQL IDE.
--config, -c(none)Path to a TOML file with service configuration; see below.

TOML [limits]

The [limits] table controls how heavy a single query can be.

KeyDefaultDescription
max-query-depth20Maximum depth of nodes in a request.
max-query-nodes300Maximum number of nodes in a request.
max-output-nodes100000Maximum number of output nodes allowed in the response.
max-tx-payload-size174763Maximum size in bytes for txBytes and signatures in executeTransactionBlock, and txBytes in dryRunTransactionBlock.
max-query-payload-size5000Maximum size in bytes of the JSON payload of a read request (excluding max-tx-payload-size).
max-db-query-cost20000Queries whose EXPLAIN cost exceeds this are logged. Units are Postgres planner units (≈ one sequential page access).
default-page-size20Page size used when a paginated query does not specify one.
max-page-size50Maximum page size a paginated query may request.
mutation-timeout-ms74000Server-side timeout for mutation requests, in milliseconds. The underlying transaction may continue to execute on the fullnode after the timeout fires.
request-timeout-ms40000Server-side timeout for read requests, in milliseconds.
max-type-argument-depth16Maximum nesting of generic type arguments.
max-type-argument-width32Maximum number of type parameters a type can have.
max-type-nodes256Maximum size of a fully qualified type.
max-move-value-depth128Maximum depth of a Move value.
max-transaction-ids1000Maximum number of transaction ids accepted by TransactionBlockFilter and transactionBlocksByDigests.
max-scan-limit100000000Maximum number of candidate rows the database scans when assembling a page.

TOML — other tables

KeyDefaultDescription
disabled-features[]List of functional groups removed from the schema. See Disabled features.
background-tasks.watermark-update-ms500How often the watermark task refreshes the maximum readable checkpoint and epoch, in milliseconds.
iota-names.*mainnet IDsOn-chain name registry the service resolves names against. See IOTA Names resolution.
versionscrate major.minorGraphQL schema versions advertised through serviceConfig.availableVersions.