Skip to main content

Local development & testing

Build with Local Tableland to quickly iterate with Tableland using a local environment.

Developers can leverage a local-only instance of the Tableland network that spins up a local Hardhat node in the process. This allows for quickly iterating while developing (create, write, and read local tables) and also allows for integrating Local Tableland into your testing flow.

Installation & setup

Install @tableland/local within a project:

npm install -g @tableland/local

You can simply run the following command, which will spin up a Local Tableland and Hardhat node for local-only prototyping.

npx local-tableland

Under the hood, a few things will then happen:

  1. A local Hardhat node is spun up (at http://localhost:8545), along with 20 test accounts (public-private key pairs). Note the chainId is 31337 for the local blockchain.
  2. The TablelandTables.sol contract is deployed on the local network, allowing for smart contract calls for table creates and writes.
  3. A local Tableland network node is spun up, which will process the events from the TablelandTables.sol contract, materializes the SQL instructions, and allows for read queries locally (via REST API at http://localhost:8080).


If you’re using the SDK, connecting to the local Tableland network comes as part of the Signer connection. For example, with a browser wallet, the following would prompt for a connection, and assuming the wallet connects to a local Hardhat node running on and chain 31337, then it should be good to go.

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

const privateKey =
"59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"; // Your private key
const wallet = new Wallet(privateKey);
// To avoid connecting to the browser wallet (locally, port 8545),
// replace the URL with a provider like Alchemy, Infura, Etherscan, etc.
const provider = getDefaultProvider(""); // For example: "${process.env.YOUR_ALCHEMY_KEY}"
const signer = wallet.connect(provider);
// Connect to the database
const db = new Database({ signer });

Thus, creating, writing to, and reading from tables will all happen locally, without needing to connect to a testnet or mainnet chain. See the JavaScript SDK documentation for more SDK usage details.

Smart contracts

Since running npx local-tableland starts a hardhat node, developers can bypass the usage of npx hardhat node. For example, if you’re following hardhat’s deployment instructions, the following steps would be used to deploy your smart contracts:

npx local-tableland
## In a separate window, deploy the contracts locally
npx hardhat run scripts/deploy.js --network localhost

Alternatively, check the Hardhat quickstart for using the @tableland/hardhat plugin, which differs a bit from this usage.


Once the local node is running, tables can be accessed just as they are with the Tableland testnet and mainnet network. But, instead of the Tableland-hosted gateway, the local Tableland network is accessible at http://localhost:8080. The network will always create a healthbot table as the first table, so you can easily test out this functionality from the get go.

You can use the query endpoint to retrieve some data (not the chain ID is 31337):

curl http://localhost:8080/api/v1/query?statement=select%20counter%20from%20healthbot_31337_1

Which should return:

"counter": 1

All of the Tableland APIs are available at this URL, so anything that you’d like to develop and test out locally is available on testnet / mainnet chains (and vice versa). Check out the REST API docs for more details! And if you’re unfamiliar with the encoding used, see the docs on URI Encoding.


With the CLI, you should specify local-tableland with the init command or pass it as a --chain flag. Once doing so, you will be able to create, write, and read tables locally, once the nodes are running:

tableland read "select * from healthbot_31337_1"

Wallets & nonce issues

When testing locally, you may want to import an account created during the hardhat process. These are publicly known accounts such that exposing the private key is not an issue as they’re meant for testing purposes. Do not use the first account (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266) since this can lead to nonce issues. This account is the one "owned" by the validator upon creating the first healthbot table on the network, so it should be reserved for that and that alone.

[Registry] Accounts
[Registry] ========
[Registry] WARNING: These accounts, and their private keys, are publicly known.
[Registry] Any funds sent to them on Mainnet or any other live network WILL BE LOST.
[Registry] Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
[Registry] Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
[Registry] Account #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
[Registry] Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

For example, with the second account created, import the private key value into your wallet, which will allow you to use the 10000 test ETH provided:

  • Public key: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
  • Private key: 59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

For more information, dive into the Local Tableland documentation.