Storing Data on Arkiv
This guide targets the Tiramisu testnet. Check Networks for current testnets and faucets.
In this part, you build the Node.js script that publishes data. This script fetches data from CoinGecko and uses the Arkiv SDK to write it to the blockchain.
Open backend/index.js and build it step by step.
Step 1: Imports and Client Setup
Section titled “Step 1: Imports and Client Setup”First, import the functions that you need from the Arkiv SDK, then set up the wallet client. This client authenticates you with your private key, so you can write data.
Directorybackend/
- index.js
- .env
Directoryfrontend/
- …
- package.json
import { createWalletClient } from '@arkiv-network/sdk';import { tiramisu } from '@arkiv-network/sdk/chains';import { http } from 'viem';import { privateKeyToAccount } from 'viem/accounts';
// Load the private key from the environment file.const privateKey = process.env.PRIVATE_KEY;if (!privateKey) { throw new Error("PRIVATE_KEY is not set in the .env file.");}
// Create an account object from the private key.const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.const client = createWalletClient({ chain: tiramisu, // Use the Tiramisu testnet. transport: http(), account: account,});
console.log(`Backend service connected as: ${client.account.address}`);Run your script to see your wallet address:
node --env-file backend/.env backend/index.jsThe output looks like this: Backend service connected as: 0x1234...
Step 2: Get Test GLM
Section titled “Step 2: Get Test GLM”Before you create entities on Arkiv, you need test GLM to pay for gas fees. Tiramisu uses GLM as its native gas token.
Get test GLM from the Arkiv faucet. Use the address that the previous step printed. If the faucet does not work for you, ask in the Arkiv Discord.
Step 3: Fetching Data from CoinGecko
Section titled “Step 3: Fetching Data from CoinGecko”Next, add a function that calls the CoinGecko API and fetches your cryptocurrency data.
import { createWalletClient } from '@arkiv-network/sdk';import { tiramisu } from '@arkiv-network/sdk/chains';import { http } from 'viem';import { privateKeyToAccount } from 'viem/accounts';import axios from 'axios';
// Load the private key from the environment file.const privateKey = process.env.PRIVATE_KEY;if (!privateKey) { throw new Error("PRIVATE_KEY is not set in the .env file.");}
// Create an account object from the private key.const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.const client = createWalletClient({ chain: tiramisu, // Use the Tiramisu testnet. transport: http(), account: account,});
console.log(`Backend service connected as: ${client.account.address}`);
// Construct the CoinGecko API URLconst params = new URLSearchParams({ vs_currency: 'usd', ids: 'bitcoin,ethereum,golem', sparkline: 'false'});const COINGECKO_URL = `https://api.coingecko.com/api/v3/coins/markets?${params}`;
async function fetchCryptoData() { try { const response = await axios.get(COINGECKO_URL); console.log('Successfully fetched data from CoinGecko.'); return response.data; } catch (error) { console.error('Error fetching data from CoinGecko:', error.message); return []; // Return an empty array on failure. }}Step 4: Uploading Data to Arkiv
Section titled “Step 4: Uploading Data to Arkiv”This is the core of the backend. This function takes all the crypto data and creates entities on Arkiv in one batch operation using the executeBatch method.
- Payload: the main data that you want to store, structured as JSON.
- Attributes: key-value pairs that act as tags or metadata. Attributes make your data queryable later.
- expires: entities can be configured to expire automatically after a specific time. After the block at which an entity expires, queries do not return it.
import { createWalletClient } from '@arkiv-network/sdk';import { tiramisu } from '@arkiv-network/sdk/chains';import { ExpirationTime, jsonToPayload } from '@arkiv-network/sdk';import { http } from 'viem';import { privateKeyToAccount } from 'viem/accounts';import axios from 'axios';
// Load the private key from the environment file.const privateKey = process.env.PRIVATE_KEY;if (!privateKey) { throw new Error("PRIVATE_KEY is not set in the .env file.");}
// Create an account object from the private key.const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.const client = createWalletClient({ chain: tiramisu, // Use the Tiramisu testnet. transport: http(), account: account,});
console.log(`Backend service connected as: ${client.account.address}`);
// Construct the CoinGecko API URLconst params = new URLSearchParams({ vs_currency: 'usd', ids: 'bitcoin,ethereum,golem', sparkline: 'false'});const COINGECKO_URL = `https://api.coingecko.com/api/v3/coins/markets?${params}`;
async function fetchCryptoData() { try { const response = await axios.get(COINGECKO_URL); console.log('Successfully fetched data from CoinGecko.'); return response.data; } catch (error) { console.error('Error fetching data from CoinGecko:', error.message); return []; // Return an empty array on failure. }}
async function uploadDataToArkiv(cryptoData) { if (cryptoData.length === 0) { console.log("No crypto data to upload."); return; }
try { // Create payload objects for all tokens const createPayloads = cryptoData.map(tokenData => { const { id, current_price, market_cap, price_change_percentage_24h } = tokenData; return { payload: jsonToPayload({ price: current_price, marketCap: market_cap, change24h: price_change_percentage_24h, timestamp: Date.now(), }), contentType: 'application/json', attributes: { token: id, // 'bitcoin', 'ethereum', or 'golem' }, expires: ExpirationTime.fromHours(3), // Data expires after 3 hours. }; });
const result = await client.executeBatch({ creates: createPayloads });
// Log success for each created entity result.createdEntities.forEach((entityKey, index) => { const tokenId = cryptoData[index].id; console.log(`Created entity for ${tokenId}. Key: ${entityKey}`); });
} catch (error) { console.error('Failed to create entities:', error.message); }}Step 5: The Main Loop
Section titled “Step 5: The Main Loop”Finally, create a main loop that ties everything together. This function fetches the data and uploads all tokens in one batch operation.
import { createWalletClient } from '@arkiv-network/sdk';import { tiramisu } from '@arkiv-network/sdk/chains';import { ExpirationTime, jsonToPayload } from '@arkiv-network/sdk';import { http } from 'viem';import { privateKeyToAccount } from 'viem/accounts';import axios from 'axios';
// Load the private key from the environment file.const privateKey = process.env.PRIVATE_KEY;if (!privateKey) { throw new Error("PRIVATE_KEY is not set in the .env file.");}
// Create an account object from the private key.const account = privateKeyToAccount(privateKey);
// Create a wallet client to interact with Arkiv.const client = createWalletClient({ chain: tiramisu, // Use the Tiramisu testnet. transport: http(), account: account,});
console.log(`Backend service connected as: ${client.account.address}`);
// Construct the CoinGecko API URLconst params = new URLSearchParams({ vs_currency: 'usd', ids: 'bitcoin,ethereum,golem', sparkline: 'false'});const COINGECKO_URL = `https://api.coingecko.com/api/v3/coins/markets?${params}`;
async function fetchCryptoData() { try { const response = await axios.get(COINGECKO_URL); console.log('Successfully fetched data from CoinGecko.'); return response.data; } catch (error) { console.error('Error fetching data from CoinGecko:', error.message); return []; // Return an empty array on failure. }}
async function uploadDataToArkiv(cryptoData) { if (cryptoData.length === 0) { console.log("No crypto data to upload."); return; }
try { // Create payload objects for all tokens const createPayloads = cryptoData.map(tokenData => { const { id, current_price, market_cap, price_change_percentage_24h } = tokenData; return { payload: jsonToPayload({ price: current_price, marketCap: market_cap, change24h: price_change_percentage_24h, timestamp: Date.now(), }), contentType: 'application/json', attributes: { token: id, // 'bitcoin', 'ethereum', or 'golem' }, expires: ExpirationTime.fromHours(3), // Data expires after 3 hours. }; });
const result = await client.executeBatch({ creates: createPayloads });
// Log success for each created entity result.createdEntities.forEach((entityKey, index) => { const tokenId = cryptoData[index].id; console.log(`Created entity for ${tokenId}. Key: ${entityKey}`); });
} catch (error) { console.error('Failed to create entities:', error.message); }}
async function runUpdateCycle() { console.log("\n--- Starting new update cycle ---"); const cryptoData = await fetchCryptoData();
if (cryptoData.length > 0) { // Upload all tokens in a single executeBatch call await uploadDataToArkiv(cryptoData); }}
// Run the cycle on start, and then every 60 seconds.runUpdateCycle();setInterval(runUpdateCycle, 60000); // 60000 ms = 60 secondsStep 6: Run the Backend
Section titled “Step 6: Run the Backend”The backend is complete. Run it from your terminal:
node --env-file backend/.env backend/index.jsThe logs show your address and each entity it creates. Do not stop the script: it must stay active. In the next section, you build a frontend to see the data.