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.
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.
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_createEffectin favor ofcreateEffect - Renamed transaction field
kindtotype - For block handlers:
block.chainIdis removed in favor ofcontext.chain.id
config.yaml Changes
- Renamed
networkstochains - Renamed
confirmed_block_thresholdtomax_reorg_depth - Removed
unordered_multichain_modeflag, replaced withmultichain: ordered | unordered(default:unordered) - Removed
loadersoption (now always enabled via Preload Optimization) - Removed
preload_handlersoption (now always enabled) - Removed
preRegisterDynamicContractsoption
Environment Variable Changes
- Removed
UNSTABLE__TEMP_UNORDERED_HEAD_MODEenvironment variable - Removed
UNORDERED_MULTICHAIN_MODEenvironment variable - Removed
MAX_BATCH_SIZEenvironment variable (usefull_batch_sizein config.yaml instead)
Generated Code Changes
- Removed
chaintype in favor ofChainId(now a union type instead of a number) - Removed internal
ContractTypeenum (allows longer contract names) - Removed
getGeneratedByChainId(replacement coming in a future alpha version)
Metrics Changes
- Renamed
chain_block_heightPrometheus metric toenvio_indexing_known_height
Migration Guide
Step 0: Prepare on V2 (Recommended)
Before upgrading to V3, we recommend preparing your project while still on V2:
- Upgrade to v2.32.6 and enable Preload Optimization:
# config.yaml
preload_handlers: true
-
If you were using loaders, migrate them to Preload Optimization following the Migrating from Loaders guide.
-
Verify your indexer works correctly with
pnpm devbefore 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"
}
}
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"]
}
}
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_threshold→max_reorg_depth
Remove deprecated options:
Remove the following options from your config if present:
loaders— now always enabled via Preload Optimizationpreload_handlers— now always enabledpreRegisterDynamicContracts— no longer neededunordered_multichain_mode— replaced withmultichainoption
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_MODEUNORDERED_MULTICHAIN_MODEMAX_BATCH_SIZE— usefull_batch_sizein config.yaml instead
Step 5: Update Handler Code
Rename deprecated APIs:
| V2 (Deprecated) | V3 |
|---|---|
experimental_createEffect | createEffect |
block.chainId (in block handlers) | context.chain.id |
transaction.kind | transaction.type |
chain type | ChainId |
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: truein config.yaml - Migrate from loaders if applicable (guide)
- Verify indexer works with
pnpm dev
Dependencies:
- Update Node.js to >=22
- Add
"type": "module"topackage.json← Required for V3! - Update
enviodependency to3.0.0-alpha.3 - Update
engines.nodeto>=22.0.0inpackage.json - Update
tsconfig.jsonfor ESM support - Replace
ts-mocha/ts-nodewithtsxif using tests
config.yaml:
- Rename
networkstochains - Rename
confirmed_block_thresholdtomax_reorg_depth - Remove
unordered_multichain_mode(now default) - Remove
loadersandpreload_handlersoptions - Remove
preRegisterDynamicContractsoption
Environment Variables:
- Remove
UNSTABLE__TEMP_UNORDERED_HEAD_MODE - Remove
UNORDERED_MULTICHAIN_MODE - Remove
MAX_BATCH_SIZE(usefull_batch_sizein config.yaml)
Handler Code:
- Replace
experimental_createEffectwithcreateEffect - Replace
block.chainIdwithcontext.chain.idin block handlers - Replace
transaction.kindwithtransaction.type - Update usage of
chaintype toChainId - Remove usage of
getGeneratedByChainId(replacement coming soon)
Verify:
- Run
pnpm envio codegenandpnpm devto verify
Getting Help
If you encounter any issues during migration, join our Discord community for support.
Release Notes
For detailed release notes, see: