Indexing Ethereum data with The Graph

Being able to consume relevant and concise data is essential to virtually any web application, the same applies to decentralized applications (or dApps), which are built on top of the blockchain, using it as the primary source of information. However, as opposed to typical data storage systems, some aspects of the blockchain make it time-consuming and painfully challenging to query through data. This is the problem The Graph strives to solve. This blog post will go over how and why to use The Graph to index relevant data from the Ethereum blockchain.

A few terms in this article may be unclear for anyone unfamiliar with the whole Web3 context. So, before we dive in any further, let's go over some key concepts:


A blockchain is essentially a digital ledger of transactions that is duplicated and distributed across the entire network of computer systems on the blockchain.


Ethereum is a decentralized blockchain platform that executes and verifies application code, called smart contracts.


DApps are web applications built on open, decentralized, peer-to-peer infrastructure services. The concept of DApps is meant to take the Web to what some believe to be its next evolutionary stage. The term used to describe this evolution is web3, meaning the third version of the web.

Smart contracts

A smart contract is a piece of code running on Ethereum. It’s called a “contract” because this code can control valuable things like ETH or other digital assets.

Diving in

As mentioned before, a blockchain is an immutable, shared, write-only, distributed ledger. It is a database that stores encrypted blocks of data in a decentralized network. These blocks are chained together, each block consisting of records of all new transactions processed in that block and information from the previous block.

Data stored in the blockchain is distributed by nature, making it challenging to read anything other than basic data directly. Typically, one would need to travel down the chain, block by block, to access simple information such as an account's transaction history. Complexity exponentially escalates when developing a dApp that requires large amounts of data processing.

Currently, it is possible to use centralized platforms that facilitate consuming data from the blockchain. That, however, violates the very nature of a decentralized application. With The Graph, developers can use subgraphs to provide their dApps with relevant data instead of being limited by third-party blockchain data providers.

What is The graph

The Graph is a decentralized protocol for indexing and querying blockchain data. It enables collecting, processing, accessing relevant information that is difficult to query directly and exposing them through open GraphQL based APIs. These APIs are called subgraphs and can be tailored to attend to an application's specific needs.

One of the main advantages of using The Graph is that it can help overcome the initial technological barrier for developers to start working on the Web3 space since it provides a way to query data with GraphQL, a fairly used web 2.0 technology, which provides ordering and filtering out-of-the-box.

The Graph also provides many available, ready-to-use subgraphs like Uniswap, LivePeer, Enzyme, and others. To use these subgraphs one can simply make requests to their API URL. For the full list of available subgraphs, visit the Graph explorer. It is also possible to test public subgraphs directly from the explorer using the playground feature.

Starting with The graph

The Graph documentation already offers thorough examples on how to create a subgraph from start to finish so we will skip some steps. For explanation purposes, however, the code samples used in this blog post illustrate a simple subgraph for tracking activity for the Meebits NFT smart contract.

To check more about the Meebits contract and other contracts, visit Etherscan, a web explorer for the Ethereum network. Simply type in “Meebits“ into the search field and it will display key pieces of information on the contract address, last transactions, and much more.

Creating a Subgraph

The first step to creating a subgraph is defining what data we want to index. This is specified in a YAML file, called manifest. The manifest is the main subgraph configuration. It defines the addresses for the smart contracts the subgraph should consider, which contract events or functions to listen to, and which handlers will transform these events into entities to be stored. A subgraph manifest file looks like the following:

specVersion: 0.0.1
description: Meebits Subgraph
  file: ./schema.graphql
  - kind: ethereum/contract
    name: Meebits
    network: mainnet
      address: "0x7Bd29408f11D2bFC23c34f18275bBf23bB716Bc7"
      abi: Meebits
      kind: ethereum/events
      apiVersion: 0.0.5
      language: wasm/assemblyscript
        - User
        - NFT
        - name: Meebits
          file: ./abis/Meebits.json
        - event: Transfer(indexed address,indexed address,indexed uint256)
          handler: handleTransfer
      file: ./src/mapping.ts

Every time a Meebit token is transferred from one account to another, it emits a Transfer event. With this manifest, our subgraph will listen to each of these events and then call the handleTransfer method, which will be defined later.

The next step is to define a GraphQL schema. This does require some knowledge of how entity types are declared. Gladly, GraphQL provides extensive documentation and an active community around it. This schema will define the data model for the subgraph, the format in which this data can be queried, and also be used to generate fields for querying instances of that entity type. It allows indexing data in a format made to suit the application’s UI. For example, to save a list of Meebits NFTs owned by a specific account, here is what the schema would look like:

type User @entity {
  id: ID!
  nfts: [NFT!]! @derivedFrom(field: "owner")

type NFT @entity {
  id: ID!
  owner: User!
  URI: String!

Finally, we should define our mappings. A mapping, in this context, is the code that will handle events from the contracts and allows saving to the entities defined in the schema. These mappings are written in AssemblyScript, which is a subset of Typescript. They are what glue things together in the subgraph and implement the methods specified previously in the manifest.

import {
} from "../generated/Meebits/Meebits"
import { NFT, User } from "../generated/schema"

export function handleTransfer(event: Transfer): void {
  let contract = Meebits.bind(event.address)
  const tokenId = event.params.tokenId;
  const userAddress =
  // Load existing NFT object
  let nft = new NFT(tokenId.toHex());
  if (!nft) {
    // create new NFT in case it does not exist
    nft = new NFT(tokenId.toHex());

  // Load existing User object
  let user = User.load(userAddress)
    // Create new user in case it does not exist
    user = new User(userAddress)
  nft.owner =
  // Calls the contract to save the NFT URI
  nft.URI = contract.tokenURI(event.params.tokenId);

After that, we can use the following query to retrieve the information from the subgraph:

    nfts {

The graph offers a command line scaffolding tool to walk through the steps for creating a subgraph and generating most files.

Deploying a Subgraph

After creating a subgraph, it needs to be deployed. The graph offers a Hosted service, where subgraphs can be published. Follow the docs on how to deploy a subgraph here. Before publishing, one can also use a query URL for development purposes.

The demo Meebit subgraph has been deployed and can be found here. Try it out using the hosted service playground.

Final thoughts

As with any other technology, there are some drawbacks one must consider:

  • Local deployment is possible, but indexing a subgraph locally can take up to a few hours, slowing the development process.
  • Despite being a subset of Typescript, AssemblyScript is an even newer, still evolving technology, and as such it has limitations. As of now, some important features are not yet supported, such as closures and exception handling. Learn more about some AssembyScript limitations on The graph docs or check here for the full list of language features.
  • Debugging is challenging, especially for errors that occur while indexing since logs are not optimal.

Nevertheless, The graph is growing and gaining substantial interest from the community as an important tool for developing dApps. As of now, its hosted service is home to over 10k subgraphs, and that number keeps rising, making it, at the very least, worth checking out.


Mariane Pastor

Fullstack Developer at Vinta Software.