Top tips
Find most useful tips and patterns to help you get the most out of Apibara.
General
Watching a file
You can watch files for changes during indexer execution, which is useful for development workflows and dynamic configuration updates. Here's an example of how to implement file watching using Node.js's built-in fs.watch:
import { watch } from "node:fs";
import { StarknetStream } from "@apibara/starknet";
import { defineIndexer, reloadIndexer } from "apibara/indexer";
import { useLogger } from "apibara/plugins";
import type { ApibaraRuntimeConfig } from "apibara/types";
export default function (runtimeConfig: ApibaraRuntimeConfig) {
return defineIndexer(StarknetStream)({
streamUrl: "https://mainnet.starknet.a5a.ch",
finality: "accepted",
startingBlock: 10_000n,
filter: {
// ...
},
hooks: {
"run:before": ({ abortSignal }) => {
const logger = useLogger();
logger.info("=== FILE WATCHER SET UP ===");
watch("./tmp/test", { signal: abortSignal }, () => {
logger.info("=== FILE CHANGED ===");
reloadIndexer();
});
},
},
async transform({ endCursor, finality }) {
// ...
},
});
}⚠️ Important warnings:
-
Use
watchinstead ofwatchFile: When watching files, usefs.watch()instead offs.watchFile(). Thewatchfunction works fine withreloadIndexer()oruseIndexerContext(), butwatchFilehas compatibility issues withAsyncLocalStoragefromnode:async_hookswhich is used internally by Apibara. -
If you must use
watchFile, make sure to callfs.unwatchFile()before setting up a new callback to prevent callback accumulation during indexer reloads and ensure latest context is used. -
Multiple triggers per file change: Watch callbacks may be triggered multiple times for a single file change due to OS-level differences. Different operating systems handle file system events differently, so your callback might fire 2-3 times for one modification.
💡 Best practices:
- Use the
abortSignalparameter from hooks to properly clean up watchers when the indexer stops or reloads. This prevents orphaned watchers and ensures clean shutdown. - The abort signal is automatically triggered when the indexer is stopped or killed, making it perfect for cleanup scenarios during indexer reloads.
Reloading the indexer
You can programmatically reload your indexer using the reloadIndexer() function:
import { watch } from "node:fs";
import { StarknetStream } from "@apibara/starknet";
import { defineIndexer, reloadIndexer } from "apibara/indexer";
import { useLogger } from "apibara/plugins";
import type { ApibaraRuntimeConfig } from "apibara/types";
export default function (runtimeConfig: ApibaraRuntimeConfig) {
return defineIndexer(StarknetStream)({
streamUrl: "https://mainnet.starknet.a5a.ch",
finality: "accepted",
startingBlock: 10_000n,
filter: {
// ...
},
async transform({ endCursor, finality }) {
// ...
if (endCursor?.orderKey === 150000n) {
reloadIndexer();
}
},
});
}