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
executeTransactionBlockoperation 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-urlis 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-serversubcommand. - 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
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.checkpointstransactionBlock,transactionBlocksByDigeststransactionBlockswith filteratCheckpoint,affectedAddress, ortransactionIds, and the matchingCheckpoint.transactionBlocksandAddress.transactionBlocks(AFFECTEDrelation)eventswith filtertransactionDigestobjectat a past version, including dynamic fields resolved at a parent object's version
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:
| Name | Paths |
|---|---|
coins | Query.coinMetadata; Address.balance/balances/coins; Object.balance/balances/coins; Owner.balance/balances/coins |
dynamic-fields | Object.dynamicField/dynamicFields/dynamicObjectField; Owner.dynamicField/dynamicFields/dynamicObjectField |
subscriptions | Subscription.events; Subscription.transactions |
system-state | Query.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
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Host the GraphQL endpoint binds to. |
--port, -p | 8000 | Port the GraphQL endpoint binds to. |
--db-url, -d | postgres://postgres:postgrespw@localhost:5432/iota_indexer | Postgres connection URL. |
--db-pool-size | 10 | Number of Postgres connections held in the pool. |
--prom-host | 0.0.0.0 | Host the Prometheus metrics endpoint binds to. |
--prom-port | 9184 | Port the Prometheus metrics endpoint binds to. |
--skip-migration-consistency-check | false | Skip the start-up check that local migrations match the database. |
--max-available-range | 9000 | Maximum 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-size | 100 | Experimental. Maximum number of keys per batch request to the fallback KV store. Also FALLBACK_KV_MULTI_FETCH_BATCH_SIZE env var. |
--fallback-kv-concurrent-fetches | 10 | Experimental. Maximum number of concurrent batch requests to the fallback KV store. Also FALLBACK_KV_CONCURRENT_FETCHES env var. |
--fallback-kv-cache-size | 100000 | Experimental. In-memory cache size for historic fallback. Also FALLBACK_KV_CACHE_SIZE env var. |
--ide-title, -i | IOTA GraphQL IDE | Title 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.
| Key | Default | Description |
|---|---|---|
max-query-depth | 20 | Maximum depth of nodes in a request. |
max-query-nodes | 300 | Maximum number of nodes in a request. |
max-output-nodes | 100000 | Maximum number of output nodes allowed in the response. |
max-tx-payload-size | 174763 | Maximum size in bytes for txBytes and signatures in executeTransactionBlock, and txBytes in dryRunTransactionBlock. |
max-query-payload-size | 5000 | Maximum size in bytes of the JSON payload of a read request (excluding max-tx-payload-size). |
max-db-query-cost | 20000 | Queries whose EXPLAIN cost exceeds this are logged. Units are Postgres planner units (≈ one sequential page access). |
default-page-size | 20 | Page size used when a paginated query does not specify one. |
max-page-size | 50 | Maximum page size a paginated query may request. |
mutation-timeout-ms | 74000 | Server-side timeout for mutation requests, in milliseconds. The underlying transaction may continue to execute on the fullnode after the timeout fires. |
request-timeout-ms | 40000 | Server-side timeout for read requests, in milliseconds. |
max-type-argument-depth | 16 | Maximum nesting of generic type arguments. |
max-type-argument-width | 32 | Maximum number of type parameters a type can have. |
max-type-nodes | 256 | Maximum size of a fully qualified type. |
max-move-value-depth | 128 | Maximum depth of a Move value. |
max-transaction-ids | 1000 | Maximum number of transaction ids accepted by TransactionBlockFilter and transactionBlocksByDigests. |
max-scan-limit | 100000000 | Maximum number of candidate rows the database scans when assembling a page. |
TOML — other tables
| Key | Default | Description |
|---|---|---|
disabled-features | [] | List of functional groups removed from the schema. See Disabled features. |
background-tasks.watermark-update-ms | 500 | How often the watermark task refreshes the maximum readable checkpoint and epoch, in milliseconds. |
iota-names.* | mainnet IDs | On-chain name registry the service resolves names against. See IOTA Names resolution. |
versions | crate major.minor | GraphQL schema versions advertised through serviceConfig.availableVersions. |