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.
  • Note that currently fallback service cannot be configured for GraphQL, so data pruned by indexer writer is not accessible through GraphQL. See 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.

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.
--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.