Integrations / Data & Indexing

Quai Subgraph Integration

Apps that need rich on-chain data — dashboards, feeds, analytics — usually reach for a subgraph. On Quai, the same outcomes come from three building blocks: GraphQL served by the node itself, Quaiscan's explorer dataset, and custom indexers streaming events over WebSockets with the quais SDK.

Node GraphQL
graph.quai.network
Explorer data
quaiscan.io · orchard.quaiscan.io
Event streams
wss://rpc.quai.network
The Graph
graph-node not yet compatible — see FAQ
01

How indexing works on Quai

A subgraph is fundamentally three promises: structured access to chain data, event-driven transformation, and a GraphQL surface your app can query. Quai delivers the first promise natively — go-quai nodes ship with a built-in GraphQL API, exposed for mainnet at graph.quai.network, covering blocks, transactions, and state without any indexing infrastructure of your own.

For the second and third promises, Quai's answer today is custom indexers built on the quais SDK. A WebSocketProvider with usePathing subscribes to logs and blocks across zone chains as they land — roughly every five seconds — and contract event listeners transform and persist exactly the entities your app needs. Backfills use queryFilter over historical ranges, mirroring how subgraph handlers replay past events. Quaiscan, the network's Blockscout-style explorer, adds a second ready-made dataset for transactions, addresses, and verified contracts.

The Graph's hosted service and graph-node do not yet index Quai — graph-node expects the eth_ RPC namespace, while Quai nodes serve quai_ methods across a hierarchy of chains. Teams that structure their indexer as event handlers writing to a database keep a clean migration path for whenever that compatibility lands.

02

Stand up an indexing pipeline

  1. 1

    Start with the node's GraphQL API

    For blocks, transactions, and account state, query the built-in GraphQL endpoint before building anything — it may already answer your question.

    terminalgraphql
    {
      block {
        number
        hash
        transactions {
          hash
          from { address }
          to { address }
          value
        }
      }
    }
  2. 2

    Subscribe to live events

    Open a WebSocket provider and attach contract event listeners. Each zone produces blocks about every five seconds, so data freshness is high.

    terminaljavascript
    import { WebSocketProvider, Contract } from "quais";
    
    const provider = new WebSocketProvider("wss://rpc.quai.network", undefined, {
      usePathing: true,
    });
    const token = new Contract(TOKEN_ADDRESS, TOKEN_ABI, provider);
    
    token.on("Transfer", (from, to, value, payload) => {
      saveTransfer({ from, to, value, block: payload.log.blockNumber });
    });
  3. 3

    Backfill history

    Replay past events in bounded ranges with queryFilter — the same pattern subgraph handlers use when syncing from a start block.

    terminaljavascript
    const BATCH = 10_000;
    for (let from = START_BLOCK; from <= latest; from += BATCH) {
      const to = Math.min(from + BATCH - 1, latest);
      const events = await token.queryFilter("Transfer", from, to);
      await persist(events);
    }
  4. 4

    Persist into your own schema

    Write transformed entities to Postgres (or your store of choice) and expose them to your app through your API layer — the part of a subgraph you fully control here.

  5. 5

    Cross-check against Quaiscan

    Use quaiscan.io as the reference dataset to validate indexer completeness, and orchard.quaiscan.io while developing against testnet.

03

What gets built on indexed data

  • Protocol dashboards

    TVL, volumes, and holder distributions aggregated from event streams — the classic subgraph workload, served from your own store.

  • Wallet activity feeds

    Per-address transfer histories assembled from indexed Transfer events across the tokens your app cares about.

  • DeFi position tracking

    Reconstruct positions from deposit, borrow, and liquidation events with five-second freshness from zone chains.

  • Explorer-style products

    Purpose-built views over the node's GraphQL API and Quaiscan data — NFT galleries, token pages, governance trackers.

04

A minimal event indexer

Live subscription plus historical backfill — the two halves of every subgraph, in one self-contained script on the quais SDK.

indexer.jsjavascript
import { WebSocketProvider, Contract } from "quais";
import { db } from "./db.js";

const provider = new WebSocketProvider("wss://rpc.quai.network", undefined, {
  usePathing: true,
});
const token = new Contract(TOKEN_ADDRESS, TOKEN_ABI, provider);

// 1. Backfill — replay history in bounded ranges
const latest = await provider.getBlockNumber();
for (let from = START_BLOCK; from <= latest; from += 10_000) {
  const to = Math.min(from + 9_999, latest);
  for (const e of await token.queryFilter("Transfer", from, to)) {
    await db.transfers.upsert(serialize(e));
  }
}

// 2. Stream — handle new events as zone blocks land (~5s)
token.on("Transfer", async (from, to, value, payload) => {
  await db.transfers.upsert({
    from,
    to,
    value: value.toString(),
    block: payload.log.blockNumber,
  });
});
05

Frequently asked questions

Can I deploy a Graph Protocol subgraph to Quai Network?

Not yet. graph-node communicates with chains through the eth_ JSON-RPC namespace, while Quai nodes expose quai_ methods across a hierarchy of zone chains. The equivalent outcomes today come from the node's built-in GraphQL API, Quaiscan data, and custom event indexers on the quais SDK.

Does Quai have a native GraphQL API?

Yes. go-quai nodes include a built-in GraphQL interface — exposed for mainnet at graph.quai.network — covering blocks, transactions, and state. For many read-heavy applications this removes the need for separate indexing infrastructure entirely.

How do I index historical events on Quai?

Use queryFilter (or getLogs) over bounded block ranges with a quais provider, exactly as you would backfill with Ethers. Batch requests in ranges of a few thousand blocks and persist results to your own database.

How fresh can indexed data be?

Zone chains produce blocks roughly every five seconds, and a WebSocketProvider delivers logs as they land — so an event-driven indexer keeps dashboards within seconds of chain state.

Is there an explorer API I can use instead of running my own indexer?

Quaiscan (quaiscan.io for mainnet, orchard.quaiscan.io for testnet) provides explorer data including transactions, addresses, and verified contract sources, and stats.quai.network tracks network-level metrics. Many apps combine explorer data with a narrow custom indexer for their own contracts.

Put Quai's data to work

Query the built-in GraphQL API today, and stand up a custom indexer when your app needs its own schema.