@thru/indexer
@thru/indexer is the public root entrypoint for building persistent Thru indexers. The package exposes a single published entrypoint at the package root; there are no public subpath exports.
Install
Section titled “Install”npm install @thru/indexer @thru/replay @thru/sdk postgres drizzle-ormYou will also need a database driver and a Drizzle database/schema setup in the application that runs the indexer.
When To Use It
Section titled “When To Use It”Use @thru/indexer when you want to:
- define event streams for historical chain data
- define account streams for mutable on-chain state
- build Drizzle tables from stream definitions
- persist checkpoints so an indexer can resume after a restart
- run a background indexer that processes event and account streams together
Choose a different package when:
- you only need an ordered feed and do not want to store results yourself: use
@thru/replay - you only need direct chain reads or writes: use
@thru/sdk - you only need low-level encoding helpers: use
@thru/sdk/helpers
Main Exports
Section titled “Main Exports”| Area | Export | What it does |
|---|---|---|
| Schema builder | t, columnBuilder | Define typed columns for stream-backed Drizzle tables. |
| Validation | generateZodSchema, validateParsedData | Validate parsed rows during development or debugging. |
| Stream definitions | defineEventStream, defineAccountStream | Compile event and account stream definitions into runnable stream objects with .table exports. |
| Checkpoints | checkpointTable, getCheckpoint, updateCheckpoint, deleteCheckpoint, getAllCheckpoints, getSchemaExports | Store resumable progress and collect tables for migrations. |
| Runtime | Indexer | Run configured event and account streams against a database and replay client factory. |
| Shared types | ApiConfig, StreamBatch, HookContext, IndexerConfig, IndexerResult | Type stream hooks, config, and runtime results. |
Typical Workflow
Section titled “Typical Workflow”import { Indexer, checkpointTable, defineAccountStream, defineEventStream, t,} from "@thru/indexer";import { ChainClient } from "@thru/replay";
const indexer = new Indexer({ db, clientFactory: () => new ChainClient({ baseUrl: process.env.CHAIN_RPC_URL! }), eventStreams: [transfers], accountStreams: [tokenAccounts], defaultStartSlot: 0n, safetyMargin: 64, pageSize: 512,});
await indexer.start();defineEventStream is for append-only chain events. defineAccountStream is for current account state that gets updated as chain data changes. Both return compiled stream objects with a Drizzle table on .table, which keeps schema and runtime definitions aligned.
Indexer is the runtime layer. Its config requires a database client and a clientFactory, and it supports event streams, account streams, a default start slot, safety margin, page size, and optional runtime validation.
Reading Indexed Data
Section titled “Reading Indexed Data”@thru/indexer writes ordinary Drizzle tables. Query them directly from your backend.
import { desc, eq } from "drizzle-orm";import { db } from "./db";import { tokenTransferEvents } from "./schema";
const recentTransfers = await db .select() .from(tokenTransferEvents) .where(eq(tokenTransferEvents.dest, "ta...")) .orderBy(desc(tokenTransferEvents.slot)) .limit(20);Related Guides
Section titled “Related Guides”- Indexing Overview for package selection and the full indexing guide structure.
- Build an Indexer for a step-by-step guide with production concerns.
@thru/replayfor the replay primitives that power the indexer runtime.@thru/sdkfor the app-facing RPC SDK.