SolidRPC plans change on 28th October. Some prices are going up. Lock in today’s pricing and limits by activating before then. See what’s changing.
Skip to main content
Guides
Category
Indexing
Reading time
8 minutes
Published

When does an EVM app need an indexer instead of direct RPC?

A wallet showing one current balance and a portfolio page showing every transfer across months both use blockchain data, but they need different ways to retrieve it. The first can often read directly from JSON-RPC. The second needs a searchable history and a way to keep that history correct as blocks arrive.

An indexer does not replace RPC. It reads chain data, turns selected facts into an application database, and keeps that database in step with the chain. The decision is which questions to answer directly and which to materialize ahead of time. This guide makes that boundary explicit and shows the smallest reliable indexing path.

Section 01

What is the short answer?

Use direct RPC when the user asks for a known account, contract, transaction, or block and the method can return the answer within your response deadline. Use an indexer when the product repeatedly needs a filtered, sorted, joined, or paginated view over many blocks or contracts. Most applications that add an indexer still use RPC for current state, transaction submission, and verification.

Write down the exact screen or job before choosing. A current token balance, a transaction receipt, and a wallet's transfer history are three different data products. An indexer is justified by the query and recovery requirements, not by a general claim that RPC is slow.

Section 02

What can direct JSON-RPC answer well?

The Ethereum JSON-RPC reference includes methods for account state, contract calls, blocks, transaction receipts, and logs. If you already know the address, transaction hash, or block, a direct call is usually the simplest starting point. eth_getTransactionReceipt, for example, looks up one transaction by hash and returns its inclusion details or null when no receipt is found. See the Execution API definition.

Direct calls also suit a bounded scan. eth_getLogs filters by block range or a specific block hash, address, and ordered topics. It does not provide a general database query language for arbitrary application fields or a durable cursor for your business records. A small, infrequent report over a known range may need no indexer. A user-facing feed that must paginate across months probably does. The method specification defines the filter shape, while your application must test the required historical range and result size.

Section 03

What does an indexer add, and what can it not infer?

An indexer selects chain facts, decodes the fields the application needs, and stores a queryable projection. It can turn event logs into a wallet activity feed, maintain aggregates, join records with application data, and serve stable pagination without rescanning old blocks for every page load. Solidity's event documentation explains that topics make selected event arguments searchable, while other values remain in event data.

The projection is limited by its inputs. A contract event does not automatically describe every state change, and a log-only indexer cannot reconstruct an arbitrary historical balance just because it has transfer events. If the product needs state at an old block, specify that state query and its historical availability separately. If it needs internal execution details, ordinary events may not be enough. The archive RPC guide helps separate historical state from logs and receipts.

Section 04

Which product questions justify indexing?

Start with the output shape and update frequency, then choose the least complex path that meets them:

  • Current balance for one account: Start with direct RPC. The address and state question are known.
  • Inclusion of one transaction: Start with a direct receipt lookup by hash.
  • Recent events for one known contract: Try a bounded RPC scan first. A small range may be practical to read on demand.
  • Wallet activity across many contracts and months: Build an index when repeated filtering and pagination need a stored history.
  • A ranking by lifetime contract activity: Build an index because the ranking is a derived view over many events.
  • A contract value at an old block: Check historical state RPC separately. An event projection does not substitute for the state at that block, even if an indexer also serves the page.
Section 05

How do you build the smallest reliable indexer?

  1. Define one output table and the exact contracts, event signatures, fields, start block, chain, and confirmation policy needed to fill it. Record what the table cannot answer.
  2. Backfill bounded block ranges with eth_getLogs or the receipt path your data contract requires. Check block identity and expected response shape. Shrink a range or stop when a request cannot be completed, rather than treating an error as an empty result.
  3. Store source identity with each row, including chain, block hash, transaction hash, and log index where applicable. Make writes idempotent so replay does not duplicate rows.
  4. Commit the derived rows and a block-number-plus-hash cursor together. On restart, resume from that durable cursor, with an overlap that your deduplication rule can absorb.
  5. Compare the next block's parent hash with the committed block hash. On a mismatch, find a common ancestor, remove or mark orphaned rows, and replay the canonical path. The Execution API log model exposes block and transaction identity and a removed field, but a saved database still needs its own rollback procedure.
  6. Move from backfill to live updates without a gap. Notifications can prompt work, but explicit range reads from the last committed cursor establish completeness. The log backfill guide covers paging, persistence, and the live handoff in detail.
Section 06

How should the app keep direct reads and indexed views consistent?

Label the indexed view with the last processed block number and hash. If a page combines that view with a direct state read, decide whether both must describe the same block. A fresh latest read beside a projection that is several blocks behind can produce a confusing but individually valid result. Pin compatible reads to an explicit block where supported, or show the index's freshness and avoid presenting the values as one snapshot.

Choose a finality policy for each use case. A feed optimized for freshness may show provisional rows and must be able to retract them after a reorganization. A settlement report may wait for a stronger confirmation boundary. Do not hide this choice inside a generic retry rule. The block-tag guide explains how latest, safe, finalized, and explicit block references answer different questions.

Section 07

When is a direct-RPC design ready to become an indexer?

Measure the operation users actually wait for. If a page repeatedly scans the same historical ranges, needs server-side pagination or joins, cannot meet its deadline under the expected data volume, or cannot recover a missed interval without manual work, write down that requirement and materialize only the needed data. Keep direct RPC for one-off lookups and current state.

Before switching a screen, run the direct and indexed answers side by side on fixed blocks. Compare known event counts, sampled transaction identities, ordering, and the response at the chosen confirmation boundary. Restart the indexing worker, replay an overlapping range, and rehearse a reorganization. Ship the indexed read only when it can explain both its answer and its last processed block. This is a narrower and more useful acceptance test than comparing one RPC latency number with one database latency number.

Section 08

RPC or indexer decision checklist

  • Name the user question, required fields, oldest block, freshness target, and response deadline.
  • Try a direct method or bounded scan when the inputs are known and the answer is small.
  • Add a stored projection when repeated history queries, pagination, joins, or aggregates require it.
  • Confirm that the source data actually contains the facts the projection claims to show.
  • Give every indexed row a source block identity and every worker a durable cursor.
  • Test overlap, restart, missing history, and reorganization recovery before using the index for decisions.
  • State how a page reconciles direct current-state reads with an indexed view that may lag.

RPC supplies chain data. The indexer turns selected data into an application-specific view. Keep both responsibilities visible in the design.

Workload review

Want us to benchmark your current RPC stack?

Send your providers, routing setup, method mix, or monthly bills. We will compare the complete stack with one SolidRPC integration across cost, useful completion rate, failover, and operational work.