Skip to main content
warning

HyperIndex V3 is currently in alpha. While we don't plan major API changes, some features may still undergo minor breaking changes and developer experience improvements.

Migrate to HyperIndex V3 Alpha

15 full months have passed since the official HyperIndex v2.0.0. Since then, we have shipped 32 minor releases and multiple patches with zero breaking changes to the documented API. We also received PRs from 6 external contributors, grew from 1 GitHub star to 456, and saw many big projects rely on HyperIndex.

HyperIndex V3 Alpha focuses on modernizing the codebase and laying the foundation for many more months of development. This guide walks you through upgrading from V2 to V3.

New Features

CommonJS → ESM

We migrated HyperIndex from CommonJS-only to ESM-only. This enables:

  • Using the latest versions of libraries that have long since abandoned CommonJS support
  • Top-level await in handler files

Top-Level Await

Thanks to the migration to ESM, you can now use await directly in handler and other files:

import { ERC20 } from "generated";

const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";

// Load data before registering handlers
const addressesFromServer = await loadWhitelistedAddresses();

ERC20.Transfer.handler(
async ({ event, context }) => {
// ... your handler logic
},
{
wildcard: true,
eventFilters: [
{ from: ZERO_ADDRESS, to: addressesFromServer },
{ from: addressesFromServer, to: ZERO_ADDRESS },
],
}
);

Automatic Handler Registration (src/handlers)

We introduced automatic registration of handler files located in src/handlers.

Previously, you needed to specify an explicit path to a handler file for every contract in config.yaml. Now you can remove all of the paths from config.yaml and simply move the files to src/handlers. You can name the files however you want, but we suggest using contract names and having a file per contract.

If you don't like src/handlers, use the handlers option in config.yaml to customize it.

note

The explicit handler field in config.yaml still works, so you don't need to change anything immediately.

RPC for Live Indexing

Built by an external contributor @cairoeth to allow specifying live mode for an RPC data source to embrace low-latency head tracking:

rpc:
- url: https://eth-mainnet.your-rpc-provider.com
for: live

In this case, the RPC won't be used for historical sync but will join the source selection logic when entering live indexing.

Chain Info on Context

The Handler Context object provides a nice way to get chain info via chains and chain properties:

ERC20.Approval.handler(async ({ context }) => {
console.log(context.chain.id); // 1 - The chain id of the event
console.log(context.chain.isLive); // true - Whether the event chain is indexing at the head

console.log(context.chains); // { 1: {id: 1, isLive: true}, 8453: {id: 8453, isLive: false} }
});

ClickHouse Sink (Experimental)

We added experimental support for a ClickHouse Sink. Postgres still serves as the primary database, and you can additionally sink the entities to a ClickHouse database that is restart- and reorg-resistant.

Configure with environment variables: ENVIO_CLICKHOUSE_SINK_HOST, ENVIO_CLICKHOUSE_SINK_DATABASE, ENVIO_CLICKHOUSE_SINK_USERNAME, ENVIO_CLICKHOUSE_SINK_PASSWORD. Currently supported only on Dedicated Plan.

warning

Do not run multiple Sinks to the same database at the same time.

HyperSync Source Improvements

Multiple updates on the HyperSync side to achieve smaller latency and less traffic:

  • Server-Sent Events instead of polling to get updates about new blocks
  • CapnProto instead of JSON for query serialization
  • Cache for queries with repetitive filters - huge egress saving when indexing thousands of addresses

Fuel Block Handler Support

Block handlers are now supported for Fuel indexing.

Solana Support (Experimental)

HyperIndex now supports Solana with RPC as a source. This feature is experimental and may undergo minor breaking changes.

To initialize a Solana project:

pnpx envio@3.0.0-alpha.3 init solana

See the Solana documentation for more details.

pnpx envio init Improvements

  • Removed language selection to prefer TypeScript by default
  • Cleaned up templates to follow the latest good practices
  • Added new templates to highlight HyperIndex features, starting with: Feature: Factory Contract

Block Handler Only Indexers

Now it's possible to create indexers with only block handlers. Previously, it was required to have at least one event handler for it to work. The contracts field became optional in config.yaml.

Flexible Entity Fields

We no longer have restrictions on entity field names, such as type and others. Shape your entities any way you want. There are also improvements in generating database columns in the same order as they are defined in the schema.graphql.

Unordered Multichain Mode by Default

Unordered multichain mode is now the default behavior. This provides better performance for most use cases. If you need ordered multichain behavior, you can explicitly set multichain: ordered in your config.

Preload Optimization by Default

Preload optimization is now enabled by default, replacing the previous loaders and preload_handlers options. This improves historical sync performance automatically.

Breaking Changes

Node.js & Runtime

  • Node.js 22 is now the minimum required version
  • Changes in handler files don't trigger codegen on pnpm dev

Handler API Changes

  • Removed experimental_createEffect in favor of createEffect
  • Renamed transaction field kind to type
  • For block handlers: block.chainId is removed in favor of context.chain.id

config.yaml Changes

  • Renamed networks to chains
  • Renamed confirmed_block_threshold to max_reorg_depth
  • Removed unordered_multichain_mode flag, replaced with multichain: ordered | unordered (default: unordered)
  • Removed loaders option (now always enabled via Preload Optimization)
  • Removed preload_handlers option (now always enabled)
  • Removed preRegisterDynamicContracts option

Environment Variable Changes

  • Removed UNSTABLE__TEMP_UNORDERED_HEAD_MODE environment variable
  • Removed UNORDERED_MULTICHAIN_MODE environment variable
  • Removed MAX_BATCH_SIZE environment variable (use full_batch_size in config.yaml instead)

Generated Code Changes

  • Removed chain type in favor of ChainId (now a union type instead of a number)
  • Removed internal ContractType enum (allows longer contract names)
  • Removed getGeneratedByChainId (replacement coming in a future alpha version)

Metrics Changes

  • Renamed chain_block_height Prometheus metric to envio_indexing_known_height

Migration Guide

Before upgrading to V3, we recommend preparing your project while still on V2:

  1. Upgrade to v2.32.6 and enable Preload Optimization:
# config.yaml
preload_handlers: true
  1. If you were using loaders, migrate them to Preload Optimization following the Migrating from Loaders guide.

  2. Verify your indexer works correctly with pnpm dev before proceeding to V3.

This step ensures a smoother migration by validating Preload Optimization works with your handlers before the V3 upgrade.

Step 1: Update Dependencies

Node.js

Update Node.js to version 22 or higher.

package.json

Update your package.json with the following changes:

{
"type": "module",
"engines": {
"node": ">=22.0.0"
},
"dependencies": {
"envio": "3.0.0-alpha.3"
},
"devDependencies": {
"typescript": "^5.7.3"
}
}
warning

Adding "type": "module" is required for V3. Without it, your project will fail to start due to ESM import errors.

If you use testing with Mocha:

Remove ts-mocha and ts-node, then install tsx:

pnpm remove ts-mocha ts-node
pnpm add -D tsx@4.21.0

Update your test script in package.json:

{
"scripts": {
"mocha": "tsc --noEmit && NODE_OPTIONS='--no-warnings --import tsx' mocha --exit test/**/*.ts"
}
}

If you use ts-node for start script:

Replace with:

{
"scripts": {
"start": "envio start"
}
}

Step 2: Update tsconfig.json

Update your tsconfig.json to support ESM:

{
/* For details: https://www.totaltypescript.com/tsconfig-cheat-sheet */
"compilerOptions": {
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"verbatimModuleSyntax": true,

/* Strictness */
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,

/* For running Envio: */
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,

/* Code doesn't run in the DOM: */
"lib": ["es2022"]
}
}
tip

This includes additional strictness options like verbatimModuleSyntax and noUncheckedIndexedAccess. You can disable them to simplify the migration.

Step 3: Update config.yaml

Rename networks to chains:

# Before
networks:
- id: 1
contracts:
- name: MyContract
events:
- event: Transfer(address indexed from, address indexed to, uint256 value)

# After
chains:
- id: 1
contracts:
- name: MyContract
events:
- event: Transfer(address indexed from, address indexed to, uint256 value)

Update multichain mode (if applicable):

If you had unordered_multichain_mode: true, remove it — this is now the default. If you need ordered multichain behavior, explicitly set:

multichain: ordered

Rename config options:

  • confirmed_block_thresholdmax_reorg_depth

Remove deprecated options:

Remove the following options from your config if present:

  • loaders — now always enabled via Preload Optimization
  • preload_handlers — now always enabled
  • preRegisterDynamicContracts — no longer needed
  • unordered_multichain_mode — replaced with multichain option

New option for batch size:

If you were using MAX_BATCH_SIZE environment variable, use the new config option instead:

full_batch_size: 5000

Automatic Handler Registration (optional):

Optionally move your handler files to src/handlers/ and remove the explicit handler paths from config.yaml.

RPC for Live Indexing (optional):

If you want to use a dedicated RPC for live indexing:

rpc:
- url: https://eth-mainnet.your-rpc-provider.com
for: live

Step 4: Update Environment Variables

Remove these deprecated environment variables if present:

  • UNSTABLE__TEMP_UNORDERED_HEAD_MODE
  • UNORDERED_MULTICHAIN_MODE
  • MAX_BATCH_SIZE — use full_batch_size in config.yaml instead

Step 5: Update Handler Code

Rename deprecated APIs:

V2 (Deprecated)V3
experimental_createEffectcreateEffect
block.chainId (in block handlers)context.chain.id
transaction.kindtransaction.type
chain typeChainId

Removed APIs:

  • getGeneratedByChainId — replacement coming in a future alpha version

Step 6: Test Your Migration

After making all changes, run codegen and start your indexer:

pnpm envio codegen
pnpm dev

Quick Migration Checklist

Prepare (on V2):

  • Upgrade to envio@2.32.6
  • Enable preload_handlers: true in config.yaml
  • Migrate from loaders if applicable (guide)
  • Verify indexer works with pnpm dev

Dependencies:

  • Update Node.js to >=22
  • Add "type": "module" to package.json ← Required for V3!
  • Update envio dependency to 3.0.0-alpha.3
  • Update engines.node to >=22.0.0 in package.json
  • Update tsconfig.json for ESM support
  • Replace ts-mocha/ts-node with tsx if using tests

config.yaml:

  • Rename networks to chains
  • Rename confirmed_block_threshold to max_reorg_depth
  • Remove unordered_multichain_mode (now default)
  • Remove loaders and preload_handlers options
  • Remove preRegisterDynamicContracts option

Environment Variables:

  • Remove UNSTABLE__TEMP_UNORDERED_HEAD_MODE
  • Remove UNORDERED_MULTICHAIN_MODE
  • Remove MAX_BATCH_SIZE (use full_batch_size in config.yaml)

Handler Code:

  • Replace experimental_createEffect with createEffect
  • Replace block.chainId with context.chain.id in block handlers
  • Replace transaction.kind with transaction.type
  • Update usage of chain type to ChainId
  • Remove usage of getGeneratedByChainId (replacement coming soon)

Verify:

  • Run pnpm envio codegen and pnpm dev to verify

Getting Help

If you encounter any issues during migration, join our Discord community for support.

Release Notes

For detailed release notes, see: