| name | indexer-handlers |
| description | Use when writing or editing event handlers. Handler registration, context API (entity CRUD, getWhere queries, chain, log), spread updates, indexer runtime API, and common pitfalls. |
| metadata | {"managed-by":"envio"} |
Handler Syntax & Core API
ESM Project
This is an ESM project ("type": "module" in package.json). Top-level await is available. Use import/export syntax, not require.
Modification Workflow
- After any change to
schema.graphql or config.yaml → run pnpm codegen
- After any change to TypeScript files → run
pnpm tsc --noEmit
- Once compilation succeeds → run
pnpm dev to catch runtime errors
Handler Registration
import { indexer } from "envio";
indexer.onEvent(
{ contract: "MyContract", event: "Transfer" },
async ({ event, context }) => {
},
);
The first argument is the options object — contract and event names plus
optional wildcard / where / fields (see indexer-wildcard,
indexer-filters and indexer-transactions skills). The second argument is the
handler.
fields lists the block and transaction fields this handler reads. Anything not
listed is a type error — see the indexer-transactions skill.
indexer.onEvent(
{ contract: "MyContract", event: "Transfer", fields: { transaction: ["hash"] } },
async ({ event, context }) => {
event.transaction.hash;
},
);
Context API
Entity Operations
const entity = await context.Entity.get(id);
const entity = await context.Entity.getOrThrow(id);
const entity = await context.Entity.getOrCreate({ id, ...defaults });
const list = await context.Entity.getWhere({ fieldName: { _eq: value } });
const list = await context.Entity.getWhere({ fieldName: { _gt: value } });
const list = await context.Entity.getWhere({ fieldName: { _lt: value } });
const list = await context.Entity.getWhere({ fieldName: { _gte: value } });
const list = await context.Entity.getWhere({ fieldName: { _lte: value } });
const list = await context.Entity.getWhere({ : { : [value1, value2] } });
list = context..({ : { : min, : max } });
list = context..({ : { : a }, : { : b } });
context..(entity);
context..(id);
getWhere operators: _eq, _gt, _lt, _gte, _lte, _in. Multiple fields and operators combine with AND semantics. Any non-derived field is queryable — the indexer creates the matching index the first time it's queried, which pauses indexing while the index builds. Marking a field @index in schema.graphql normally avoids that pause: those indexes are created together when the backfill finishes, before the indexer reports ready. A getWhere on an @index field during backfill still builds it there and then. See indexer-schema for @index syntax.
Context Properties
context.chain.id
context.chain.isRealtime
context.isPreload
context.log
context.effect(fn, input)
Spread Operator for Updates
Entities from context.Entity.get() are read-only. Always spread:
const entity = await context.Entity.get(id);
if (entity) {
context.Entity.set({ ...entity, field: newValue });
}
indexer Runtime API
import { indexer } from "envio";
indexer.name;
indexer.chainIds;
indexer.chains[1].id;
indexer.chains[1].name;
indexer.chains[1].startBlock;
indexer.chains[1].isRealtime;
indexer.chains[1].MyContract.name;
indexer.chains[1].MyContract.addresses;
indexer.chains[1].MyContract.abi;
Common Pitfalls
Entity IDs — with disable_default_cross_chain: true a row is identified by (id, chainId), so the id only has to be unique within its chain:
const id = `${event.block.number}_${event.logIndex}`;
Without the flag — or on an entity marked @crossChain — the id is the whole key, so prefix it with ${event.chainId}_ to keep chains apart.
Entity relationships — schema uses the entity reference (token0: Token!); handlers use the _id suffix codegen adds (token0_id: token0.id), typed as the referenced entity's id. Never write the bare name (token0) in the handler, and never put _id in the schema.
Optionals — string | undefined, not string | null
Decimal normalization — ALWAYS normalize when adding tokens with different decimals.
Schema & config — see indexer-schema and indexer-configuration skills for full reference.
If something is unclear, use the envio-docs skill to search and read the latest documentation.