Skip to main content

Build an Indexer from Scratch

This guide demonstrates how SDK packages can be combined into a working indexer (called a squid). This page goes through all the technical details to make the squid architecture easier to understand. If you want to get to a working indexer ASAP, bootstrap from a template.

Prerequisites

Before you begin, ensure you have:

Project: USDT Transfers API

In this tutorial, you’ll build an indexer that tracks USDT transfers on Ethereum, saves the data to PostgreSQL, and serves it as a GraphQL API.

Required packages

From the requirements above, you’ll need these packages:
  • @subsquid/evm-stream - for retrieving Ethereum data from a SQD Network Portal
  • @subsquid/batch-processor - for running the data source against a batch handler
  • @subsquid/evm-objects - for enriching the retrieved blocks with item IDs and navigation helpers
  • @subsquid/typeorm-store, @subsquid/typeorm-codegen, and @subsquid/typeorm-migration - for saving data to PostgreSQL

Optional packages

  • @subsquid/evm-typegen - for decoding Ethereum data and useful constants such as event topic0 values
  • @subsquid/evm-abi and @subsquid/evm-codec - runtime dependencies imported by the code generated by @subsquid/evm-typegen
  • @subsquid/graphql-server / OpenReader - for serving GraphQL API

Build the Indexer

1

Initialize the project

Create a new folder and initialize a Node.js project:
Create a .gitignore file:
.gitignore
2

Install dependencies

Install the required packages:
Install development dependencies:
3

Configure TypeScript

Create a tsconfig.json file with minimal configuration:
tsconfig.json
4

Define the data schema

Create a schema.graphql file to define your data model:
schema.graphql
Generate TypeORM classes from the schema:
The TypeORM classes are now available at src/model/index.ts.
5

Set up the database

Create environment variables in a .env file:
.env
Create a docker-compose.yaml file for PostgreSQL:
docker-compose.yaml
Start the database:
Compile the TypeORM classes:
Generate and apply the database migration:
6

Generate ABI utilities

Create an abi folder for contract ABIs:
Find the USDT contract ABI on Etherscan and save it to ./abi/usdt.json.Generate TypeScript utility classes from the ABI:
The utility classes are now available in src/abi/usdt/, with an index.ts entry point. Typegen v5 removes a stale src/abi/usdt.ts file from the older single-file layout.
7

Create the data source and the batch handler

Create src/main.ts with the following code blocks:

Imports

main.ts

Data source configuration

main.ts
The Portal is public — no API key or RPC endpoint is required, and the stream covers everything from historical blocks to the real-time chain head. Note that field selection is explicit: there are no default optional fields, so we list every log field the handler reads (the decoder needs topics and data).

Database configuration

main.ts
With supportHotBlocks: true the store can consume unfinalized blocks and roll the data back automatically on chain reorganizations.

Batch handler

main.ts
augmentBlock() from @subsquid/evm-objects enriches the raw block items with unique IDs (used here as entity IDs) and navigation helpers — see Context interfaces.
Supplying a TypeormDatabase to run() causes ctx.store to be a PostgreSQL-compatible Store object.
8

Run the indexer

Compile the project:
Start the indexer:
The indexer is now processing USDT transfers from the Ethereum blockchain.
9

Start the GraphQL server

Update your .env file to include the GraphQL server port:
.env
In a separate terminal, start the GraphQL server:
The GraphQL API with GraphiQL is available at localhost:4350/graphql.

Next Steps

Congratulations! You’ve built a complete blockchain indexer from scratch.

View Complete Code

See the final code for this tutorial

Development Flow

Learn about the general development workflow
The commands in this tutorial are often abbreviated as custom sqd commands in production squids. Use the commands.json file from the EVM template as a starter.