Build Apps with Node.js
This guide will help you get started with building apps with Autonomi starting from scratch. This guide has 4 parts:
Prerequisites
First let's install the required tools to get started:
Node.js with its NPM command-line tool to install dependencies and run your code
Once Node.js is installed, let's proceed to create a local testnet.
Create a local testnet
See Local Network Setup.
Connect to the testnet with Node.js
Let's create a Node.js project that interacts with the testnet. First let's setup a working environment and install the @withautonomi/autonomi package.
npm install @withautonomi/autonomi
Then create a new Node.js file in which we will write our app:
touch main.mjs
Open up the file in your favorite editor and add the following code:
import { Client, Network, Wallet } from '@withautonomi/autonomi'
// Initialize a wallet with the testnet private key
const privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
const wallet = Wallet.newFromPrivateKey(Network(true), privateKey)
console.log("Wallet address: ", wallet.address())
console.log("Wallet balance: ", await wallet.balance())
// Connect to the network
const client = await Client.initLocal()
console.log("Connected to network!")
In your terminal, run the following command to execute the script:
node main.mjs
You should see the following output:
Wallet address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Wallet balance: 2500000000000000000000000
Connected to network!
Congrats! You've just connected to the testnet! 🎉
Upload and retrieve data with Node.js
Next up let's upload some data to the testnet and retrieve it. We will be using the Autonomi data API for this. Expanding upon our previous work, change the main.mjs
file to the following:
import { Client, Network, Wallet, PaymentOption } from '@withautonomi/autonomi'
// Initialize a wallet with the testnet private key
const privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
const wallet = Wallet.newFromPrivateKey(Network(true), privateKey)
console.log("Wallet address: ", wallet.address())
console.log("Wallet balance: ", await wallet.balance())
// Connect to the network
const client = await Client.initLocal()
console.log("Connected to network!")
// Choose to pay using our wallet
const paymentOption = PaymentOption.fromWallet(wallet)
// Upload data as public (meaning anyone can read it with the address)
const data = Buffer.from("Hello, World.")
const { cost, addr } = await client.dataPutPublic(data, paymentOption)
console.log("Data uploaded for ", cost, " testnet ANT to: ", addr)
// Wait for the data to be stored by the network
await new Promise(h => setTimeout(h, 1000))
// Later, we can retrieve the data
const retrievedData = await client.dataGetPublic(addr)
console.log("Retrieved data: ", retrievedData.toString())
For private data, use the
dataPut
anddataGet
methods instead!
Congrats! If you got this far, you are ready to start building apps that can store data on Autonomi! 🎉
Working with Registers
Registers are mutable data structures that allow you to store updateable content with versioned history. Here's how to use them:
import { Client, Network, Wallet, PaymentOption, SecretKey } from '@withautonomi/autonomi'
// Initialize client and wallet (same as before)
const privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
const wallet = Wallet.newFromPrivateKey(new Network(true), privateKey)
const client = await Client.initLocal()
const paymentOption = PaymentOption.fromWallet(wallet)
// Generate keys for register
const mainKey = SecretKey.random()
const registerKey = Client.registerKeyFromName(mainKey, "my-app-config")
// Create register content (max 32 bytes)
const initialConfig = Client.registerValueFromBytes(Buffer.from("version: 1.0"))
// Check cost and create register
const cost = await client.registerCost(registerKey.publicKey())
console.log(`Register creation cost: ${cost} AttoTokens`)
const {cost: creationCost, addr} = await client.registerCreate(
registerKey, initialConfig, paymentOption
)
console.log(`Register created at: ${addr.toHex()}`)
// Wait for network replication
await new Promise(resolve => setTimeout(resolve, 5000))
// Read current value
const currentValue = await client.registerGet(addr)
console.log(`Current config: ${currentValue}`)
// Update the register
const updatedConfig = Client.registerValueFromBytes(Buffer.from("version: 1.1"))
const updateCost = await client.registerUpdate(registerKey, updatedConfig, paymentOption)
console.log(`Update cost: ${updateCost} AttoTokens`)
// Wait for replication
await new Promise(resolve => setTimeout(resolve, 5000))
// Get complete history
const history = client.registerHistory(addr)
const allVersions = await history.collect()
console.log(`Config history (${allVersions.length} versions):`)
allVersions.forEach((version, i) => {
console.log(` ${i}: ${version}`)
})
Key Register Features:
Mutable: Can be updated with new content while preserving history
Versioned: All previous versions are permanently accessible
Owned: Only the key holder can update the register
32-byte limit: Each register value is limited to 32 bytes
Paid updates: Both creation and updates require payment
Common Register Use Cases:
Application Configuration: Store app settings that need updates
Status Tracking: Maintain current state with full history
Version Control: Track document versions
Counters: Implement distributed counters
Metadata: Store changeable file or app metadata
Register API Methods:
client.registerCost(publicKey)
- Calculate creation costclient.registerCreate(key, content, payment)
- Create new registerclient.registerGet(address)
- Get current register valueclient.registerUpdate(key, content, payment)
- Update register contentclient.registerHistory(address)
- Get version history iteratorClient.registerKeyFromName(mainKey, name)
- Generate deterministic keyClient.registerValueFromBytes(buffer)
- Create RegisterValue from Buffer
Last updated