Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Setting Up a Blockchain Indexing and Query Layer

This guide details architecting a system to index and query on-chain data efficiently. It covers using subgraphs (The Graph), custom indexers with RPC nodes, and designing schemas for complex event data.
Chainscore © 2026
introduction
GUIDE

Setting Up a Blockchain Indexing and Query Layer

A practical guide to building a custom data pipeline for on-chain applications, covering core concepts and implementation steps.

Blockchain indexing is the process of extracting, transforming, and structuring raw on-chain data into a queryable format. Unlike a standard blockchain node that provides sequential block data, an indexing layer processes this data into meaningful entities like user balances, NFT transfers, or liquidity pool events. This is essential because raw blockchain data is not optimized for the complex queries that decentralized applications (dApps) require. Popular solutions like The Graph or Covalent offer hosted services, but building your own indexer provides full control over data schema, logic, and performance.

The core architecture of an indexer involves three main components. First, a data ingestion layer connects to a node RPC endpoint (e.g., using eth_getLogs on Ethereum) to stream new blocks and event logs. Second, a processing logic (often called a subgraph or mapping) defines how to handle each event—for example, updating a database record when a Transfer event is detected. Third, a query layer, typically a GraphQL or REST API, exposes the processed data to applications. This pipeline transforms unstructured log data into structured tables you can query in milliseconds.

To set up a basic indexer, you'll start by defining a data schema for your target information. For a DeFi protocol, this might include tables for User, Pool, and SwapEvent. Next, you write handlers in a language like TypeScript or Rust that listen for specific contract events and update these tables. A common stack uses PostgreSQL for storage and Apollo Server for the GraphQL layer. You must also handle chain reorganizations by implementing logic to roll back and re-process data when a block is orphaned, ensuring data consistency.

Managing the indexing infrastructure presents key challenges. Data integrity is paramount; your indexer must be resilient to node failures and chain reorgs. Performance requires efficient database indexing and batch processing to keep up with block times. For production systems, consider using a message queue like RabbitMQ to decouple ingestion from processing. While building custom indexers is complex, it unlocks capabilities like real-time analytics, custom aggregations, and privacy-focused data handling that generic services may not provide.

prerequisites
SETUP

Prerequisites

Essential tools and knowledge required to build a blockchain indexing and query layer.

Before building an indexing and query layer, you need a foundational understanding of blockchain data structures and modern development tools. This includes proficiency with Node.js (v18 or later) and TypeScript, as they are the standard for most indexing frameworks. Familiarity with GraphQL is crucial, as it's the dominant query language for decentralized APIs. You should also be comfortable with Docker for containerized deployments and have a basic grasp of PostgreSQL or similar relational databases for storing indexed data.

You will need access to blockchain nodes to source raw data. For Ethereum, you can run a local Geth or Erigon node, or use a node provider service like Alchemy, Infura, or QuickNode. For other chains, ensure you have the correct RPC endpoint. Setting up a project with a package manager like npm or yarn is required. Initialize a new project with npm init -y and install core dependencies such as ethers.js v6 or viem for EVM interaction.

Understanding the event-driven architecture of smart contracts is non-negotiable. Indexers primarily listen for on-chain events emitted by contracts. You must know how to decode these events using a contract's Application Binary Interface (ABI). Tools like The Graph's Subgraph Manifest or Subsquid's Squid Manifest define which contracts, events, and blocks to index. Start by forking an example project from a framework's GitHub repository to see a working configuration.

For local development, set up a robust environment. Use Hardhat or Foundry to deploy test contracts and generate events to index. Configure a local PostgreSQL instance, often via Docker: docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres. Many indexing frameworks include a local graph node or processor that you must run and connect to your database. This sandbox environment is where you will test your indexing logic and queries.

Finally, plan your data schema. Before writing mapping logic, design your GraphQL schema to define the entities and relationships your API will expose. This schema dictates how your indexer transforms and stores raw blockchain data. Consider query performance from the start—indexing historical data can be time-consuming, so define your start block carefully. With these prerequisites in place, you are ready to implement a specific indexing solution.

key-concepts-text
THE GRAPH PROTOCOL

Key Concepts: Indexers, Subgraphs, and Schemas

A technical overview of the core components for querying and indexing blockchain data using The Graph.

Blockchains are optimized for consensus and security, not for efficient data querying. To answer questions like "What were the top NFT sales on Ethereum yesterday?" or "Show me all Uniswap V3 swaps for a specific token," you need an indexing layer. This layer processes raw blockchain data, organizes it into a structured database, and exposes it via a GraphQL API. The Graph is the leading decentralized protocol that provides this service, enabling developers to build applications without running complex infrastructure.

A subgraph is the core unit of work in The Graph ecosystem. It defines what data to index from a blockchain and how to transform it. A subgraph consists of three main files written in a combination of AssemblyScript and YAML: a subgraph manifest (subgraph.yaml), a GraphQL schema (schema.graphql), and mapping scripts (mapping.ts). The manifest maps smart contract events to handler functions, the schema defines the shape of the queryable data, and the mappings contain the logic to process events and populate the entities defined in the schema.

The GraphQL schema is the blueprint for your indexed data. It defines entities—the data objects you can query—and their relationships. For example, a schema for a DEX might define Swap, Token, and Pool entities. Each entity has typed fields like id: ID!, amount: BigInt!, or timestamp: Int!. The schema dictates the structure of your final API. When you query a subgraph, you use GraphQL to request specific fields from these entities, allowing for precise, nested data retrieval in a single request.

Indexers are the node operators in The Graph network. They run the indexing software that executes subgraphs. An indexer scans the blockchain for events specified in a subgraph manifest, runs the associated mapping functions to process the data, and stores the resulting entities in a queryable database. In return for this service, they earn query fees and indexing rewards in GRT tokens. For development, you can use the hosted service or a local Graph Node, but for production, decentralized applications rely on a network of independent indexers.

To set up a query layer, you first define your schema based on your application's data needs. Next, you write mappings that translate on-chain events into entity updates. After deploying your subgraph to The Graph's hosted service or a decentralized network, indexers begin syncing the data. Once synced, your application can query the subgraph's GraphQL endpoint. A typical query might fetch a user's transaction history or aggregate protocol metrics, all with sub-second latency—a task that would be prohibitively slow and complex using direct RPC calls to an Ethereum node.

ARCHITECTURE

Indexing Approach Comparison

A comparison of common methods for building a blockchain data indexing layer, highlighting trade-offs between development speed, control, and cost.

Feature / MetricManaged Service (e.g., The Graph)Custom Indexer (e.g., Subsquid, Envio)Direct RPC Queries

Development Overhead

Low

Medium

High

Data Freshness

~1 block

< 1 sec

< 1 sec

Query Flexibility

Limited to subgraph schema

Full control over logic & output

Limited to node API methods

Historical Data Access

From subgraph deployment

From any block height

Limited by node archive depth

Hosting & Maintenance

Fully managed

Self-hosted infrastructure

Node provider dependent

Cost Model

Query fees (GRT), hosting costs

Infrastructure & dev time

RPC request costs

Multi-Chain Support

Supported subgraphs per chain

Custom logic per chain

Requires multiple node providers

Complex Event Processing

ARCHITECTURE PATTERNS

Implementation by Use Case

Real-Time DEX Monitoring

Indexing DeFi protocols requires capturing events for swaps, liquidity changes, and governance votes. For a Uniswap V3 indexer, you must track Swap, Mint, and Burn events from the pool contracts. A common pattern is to use The Graph subgraphs for historical data and a custom service for real-time updates via WebSocket connections to nodes.

Key data points to index:

  • Token pair reserves and prices
  • Liquidity provider positions and fees
  • Cumulative trading volume per pool
  • Flash loan transactions on Aave/Compound
graphql
# Example GraphQL query for Uniswap V3 pool data
query GetPoolData($poolId: ID!) {
  pool(id: $poolId) {
    token0 { symbol }
    token1 { symbol }
    liquidity
    volumeUSD
    feesUSD
    swaps(first: 5, orderBy: timestamp, orderDirection: DESC) {
      amount0
      amount1
      transaction { id }
    }
  }
}

For real-time alerts, set up a service that listens for large swap events (e.g., >$100k) and triggers notifications via Discord webhooks or Telegram bots.

build-subgraph
GUIDE

How to Build and Deploy a Subgraph

A step-by-step tutorial for creating a custom data indexing layer on The Graph protocol to query on-chain data efficiently.

A subgraph defines how to ingest, process, and store blockchain data for efficient querying via GraphQL. It consists of a manifest (subgraph.yaml), schema (schema.graphql), and mapping scripts (.ts files). The manifest maps smart contract events to handler functions written in AssemblyScript, which populate entities defined in your GraphQL schema. This transforms raw, sequential blockchain logs into a structured, queryable database. The Graph's decentralized network of Indexers then hosts your subgraph, making its API publicly accessible.

To start, install the Graph CLI: npm install -g @graphprotocol/graph-cli. Initialize a new project with graph init. You'll need the contract address, ABI, and the blockchain network (e.g., mainnet, arbitrum-one). This command scaffolds the project structure. Next, define your data model in schema.graphql. Use @entity to declare data types and @derivedFrom for relationships. For example, a User entity could have a transactions field derived from a Transaction entity, enabling efficient reverse lookups.

The core logic lives in the mapping files. When your specified contract emits an event, a corresponding handler function executes. Use the generated types from the ABI to safely decode event parameters. For instance, a handleTransfer function for an ERC-20 contract would typically create or load Account entities and a Transfer entity, updating balances and recording the transaction. Always use the store API (entity.save()) to persist changes. Test mappings locally against a forked network using graph test and a framework like Matchstick.

Before deployment, authenticate with the Graph Explorer: graph auth --product hosted-service <ACCESS_TOKEN>. For the decentralized network, use graph auth --studio. Deploy using graph deploy. For the hosted service, specify your GitHub username and subgraph name. For the decentralized network, deploy to a specific version in your studio. The deployment process uploads and compiles your subgraph, signaling Indexers to begin syncing data from the start block defined in your manifest.

After deployment, use the GraphQL playground in the Explorer or Studio to query your subgraph. Write queries to fetch aggregated data, filter by specific attributes, or paginate through results. For performance, structure your schema to minimize expensive join operations and leverage @derivedFrom fields. Monitor syncing status and query metrics. For production reliability on the decentralized network, consider curating your subgraph by signaling with GRT tokens to attract high-quality Indexers.

custom-indexer
TUTORIAL

Building a Custom Indexer with Node.js

Learn how to build a custom blockchain indexer to efficiently query on-chain data, moving beyond the limitations of standard RPC nodes.

A blockchain indexer is a specialized service that processes raw on-chain data into a structured, queryable database. While nodes provide access to the current state and recent blocks, they are inefficient for complex historical queries. An indexer solves this by listening to new blocks, extracting relevant data from events and transactions, and storing it in a format optimized for your application's needs. This is essential for building features like user activity dashboards, analytics platforms, or NFT marketplaces that require fast access to aggregated historical data.

To build an indexer, you need to connect to a node, process blocks, and persist data. Start by setting up a Node.js project and installing key dependencies: ethers.js for interacting with the Ethereum Virtual Machine (EVM), a database driver like pg for PostgreSQL, and a task queue library like bull for handling block processing jobs. Your core architecture will consist of a block listener, a data extractor that decodes smart contract logs using Application Binary Interface (ABI) files, and a database writer. This separation of concerns ensures scalability and maintainability.

The indexing logic begins with a service that fetches blocks in sequence. For Ethereum, use ethers.providers.JsonRpcProvider to subscribe to new blocks. For each block, fetch its full transaction receipts to access event logs. Your indexer must decode these logs using the contract's ABI to understand the event parameters, such as token transfer amounts or swap details. It's critical to handle chain reorganizations (reorgs) by implementing a mechanism to roll back indexed data from orphaned blocks, often by tracking block hashes instead of just numbers.

For data storage, PostgreSQL with a block_number index is a robust choice for relational data. Define tables that mirror your domain, like transfers, swaps, or mints. Use database transactions to ensure data consistency when writing multiple related records from a single block. To improve performance, consider implementing a caching layer for frequently accessed data and batch database inserts. For production, run your indexer as a separate microservice with health checks and monitoring for block processing lag.

Advanced optimizations include using multicall contracts to batch RPC calls for state data, implementing backfill strategies to index historical data efficiently, and sharding your database by chain ID or contract address for horizontal scaling. Always include comprehensive logging and alerting for processing errors or chain stalls. By building a custom indexer, you gain full control over your data schema, query performance, and resilience, forming a critical backend component for any data-intensive Web3 application.

schema-design
BLOCKCHAIN INDEXING

Designing Efficient GraphQL Schemas

A practical guide to structuring GraphQL schemas for performant and scalable blockchain data querying, using The Graph protocol as a primary example.

An efficient GraphQL schema is the foundation of a usable and performant blockchain indexing service. Unlike traditional databases, blockchain data is inherently sequential and event-driven, requiring schemas designed for on-chain patterns. The core principle is to model your schema around the data's origin: entities (like User, Pool, Token) map to contract state, while events (like Swap, Transfer) represent on-chain transactions. This design directly reflects the subgraph.yaml manifest in The Graph, where you define data sources and the events they handle. A well-structured schema enables clients to fetch complex, nested data in a single request, eliminating the need for multiple RPC calls.

Start by identifying the key entities in your protocol. For a DEX subgraph, core entities might include Pair, Token, Swap, and LiquidityPosition. Define these using GraphQL's type system, specifying fields with scalar types (String, BigInt, ID) and relationships using the @entity directive. Crucially, use ID! for unique identifiers, often derived from concatenated on-chain data like a contract address and token ID. For performance, avoid storing large arrays directly on entities; instead, model one-to-many relationships bidirectionally. For example, a Pair has many Swaps, and a Swap links back to one Pair. This structure allows for efficient filtering and pagination.

Optimize for common query patterns. If users frequently need a token's current price and its last 10 swaps, your schema should allow this in one query without excessive joins. Use derived fields sparingly; calculate them in the mapping logic (e.g., totalValueLockedUSD) and store the result rather than computing on every query. Implement pagination via The Graph's automatic support for first, skip, and orderBy on entity fields. For time-series data like daily volumes, consider creating aggregated entities (e.g., PairDayData) that are updated periodically in your mappings to make historical queries fast and cheap. This denormalization is a common trade-off for read performance in indexing systems.

Follow best practices for maintainability. Use enums for known sets of values like TransactionType (SWAP, MINT, BURN). Implement interfaces for shared fields; a Transaction interface ensures all event types have id, timestamp, and blockNumber. Document your schema with comments, as they appear in the auto-generated GraphQL playground. Always test your schema against real query patterns using a local Graph Node before deployment. A poorly designed schema leads to inefficient indexing, slow queries, and costly hosting. Resources like The Graph's official documentation provide detailed examples and schema design patterns for various DeFi and NFT protocols.

BLOCKCHAIN INDEXING

Troubleshooting Common Issues

Common problems developers encounter when setting up a blockchain indexing and query layer, with solutions for The Graph, Substreams, and custom indexers.

A subgraph can fail to sync due to several common issues. First, check the manifest file (subgraph.yaml) for errors in the data source address, start block, or event signatures. A mismatch here will cause the indexer to process zero events.

Second, ensure your mapping handlers are correctly defined and not throwing errors. Use graph-node logs to look for Error processing trigger or Mapping aborted messages. Common mapping errors include:

  • Attempting to read a non-existent entity.
  • Integer overflows in calculations.
  • Incorrectly parsing complex event data or contract calls.

Third, verify your RPC endpoint is stable and provides data for the required block range. Inconsistent RPC responses or rate limiting will cause syncs to stall. For production, use a dedicated node provider like Alchemy or Infura with archival data enabled.

BLOCKCHAIN INDEXING

Frequently Asked Questions

Common technical questions and solutions for developers building or using on-chain data infrastructure.

A blockchain indexing layer is a specialized data infrastructure that processes, organizes, and serves queryable data from raw blockchain transactions. Blockchains are optimized for consensus and state transitions, not for complex queries like "show me all NFT transfers for this wallet in the last month." An indexer solves this by:

  • Listening to new blocks and event logs in real-time.
  • Transforming raw, sequential transaction data into structured databases (e.g., PostgreSQL, GraphQL).
  • Serving efficient queries via APIs, eliminating the need for developers to run their own full nodes and parse logs manually.

Without an indexer, applications must scan the entire chain history for simple data, which is slow, costly, and impractical at scale. Services like The Graph, Subsquid, and Goldsky provide these layers.

conclusion
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

You have now configured a complete blockchain indexing and query layer, transforming raw on-chain data into a structured, accessible API.

This guide walked you through the core components of a modern data pipeline: ingesting events with a subgraph on The Graph, processing and enriching data with a Ponder indexer, and exposing a secure, typed API via GraphQL. This stack provides a robust alternative to centralized data providers, giving you full control over your data logic, indexing speed, and query performance. The separation of concerns—The Graph for raw extraction, Ponder for business logic—creates a flexible and maintainable architecture.

To build on this foundation, consider these next steps. First, enhance your data model by indexing more complex contract interactions or calculating derived metrics like total value locked (TVL) or user activity over time. Second, implement real-time features using Ponder's built-in support for WebSocket subscriptions to push updates to a frontend. Third, optimize for production by setting up monitoring for your indexer's health, implementing query rate limiting on your API, and exploring database read replicas for scaling GraphQL queries.

For further learning, explore the official documentation for The Graph and Ponder. To understand the broader ecosystem, research how projects like Goldsky and Subsquid offer managed indexing services. The ability to reliably index and serve blockchain data is a critical infrastructure skill, enabling everything from sophisticated dashboards and analytics to the core logic of decentralized applications themselves.

How to Set Up a Blockchain Indexing and Query Layer | ChainScore Guides