Skip to main content

Polling for table changes

Interact directly with the Validator API to poll for table changes.

You can watch for table changes by setting up polling on a validator node. The general flow requires setting up a Database and Validator connection, defining an instance of a PollingController (via createPollingController), and using pollForReceiptByTransactionHash to check for changes based on the transaction hash generated by an onchain action. Since the Database methods already implement polling, it's most useful when you:

  • Are creating or mutating tables from smart contracts, which have no awareness of what the validator materializes.
  • Need to trigger application logic based on table changes that aren't in your control flow—likely, the smart contract use case, again.

Polling for table changes

By default, if you create table or write data with the SDK, it'll already be using this functionality under the hood. Every chain has unique values configured for the interval and timeout params, which are used to determine how often to poll and how long to wait before timing out. See the polling controller docs for more information on createPollingController works.

You can override these values, if desired. Note the Validator does not need a signer to be instantiated, but it does need a baseUrl to connect to a validator node—the getBaseUrl helper function can be used to determine the correct one for your chain.

import { Database, Validator, helpers } from "@tableland/sdk";
import { Wallet, getDefaultProvider } from "ethers";

// This step is optional since we're creating a table for demonstration purposes below
// We'll set up the signer for Hardhat / Local Tableland
const privateKey =
const wallet = new Wallet(privateKey);
const provider = getDefaultProvider(""); // Replace with your RPC URL
const signer = wallet.connect(provider);

// We'll set up a `Database` since we're creating a table with the SDK for demonstration purposes
// The `chainID` (or ethersjs chain name) is used to determine the validator node URL
const { chainId } = await provider.getNetwork();
const db = new Database({
baseUrl: helpers.getBaseUrl(chainId),

// Connect to a Tableland validator node—it only uses the `baseUrl`, which is also part of the `Database` config
const validator = new Validator(db.config);
// Create a controller to help abort the pending tx request, once it is fulfilled
const controller = helpers.createPollingController(60_000, 1500); // 60s interval, 1.5s timeout
// Send a mutating query to the network—then get the tx hash
const res = await db.prepare("create table my_table (id int);").all();
const transactionHash = res.meta.txn?.transactionHash ?? "";
// Poll the validator on a specific chain at a specific tx hash
// Perform logic (we're just logging here), then, abort the controller
.then((_) => {
console.log("Transaction was processed by the validator");
.catch((_) => {
console.log("Transaction was not processed by the validator");
return () => {

Again, this example grabbed a transaction hash from the Database's response, but it could be any transaction that is created on the Tableland network. For instance, if you create a table or write to it directly from a smart contract, you grab that transaction hash and pass it to the pollForReceiptByTransactionHash method to watch for changes.