Skip to main content
This guide shows you how to read data from the NEAR blockchain, including account information, contract state, and block data.

Prerequisites

Install the NEAR API TypeScript library:
npm install near-api-ts

Setting Up a Client

The first step is to create a client that connects to the NEAR network. You can use preset configurations for testnet or mainnet, or create a custom client.

Using Preset Clients

import { createTestnetClient, createMainnetClient } from 'near-api-ts';

// Connect to testnet
const testnetClient = createTestnetClient();

// Connect to mainnet
const mainnetClient = createMainnetClient();

Custom Client Configuration

import { createClient } from 'near-api-ts';

const client = createClient({
  transport: {
    rpcEndpoints: {
      archival: [{ url: 'https://rpc.testnet.near.org' }]
    }
  }
});

Reading Account Information

Basic Account Query

The getAccountInfo method retrieves account details including balance, storage usage, and access keys.
import { createTestnetClient } from 'near-api-ts';

const client = createTestnetClient();

const { accountInfo } = await client.getAccountInfo({
  accountId: 'example.testnet',
  atMomentOf: 'LatestFinalBlock'
});

console.log('Account Balance:', accountInfo.balance.total.near, 'NEAR');
console.log('Storage Used:', accountInfo.storageUsed, 'bytes');
console.log('Storage Available:', accountInfo.storageAvailable, 'bytes');
console.log('Locked Balance:', accountInfo.balance.locked.near, 'NEAR');

Understanding Block References

You can query account state at different points in time using block references:
// Query at latest finalized block (recommended)
const result1 = await client.getAccountInfo({
  accountId: 'example.testnet',
  atMomentOf: 'LatestFinalBlock'
});

// Query at specific block height
const result2 = await client.getAccountInfo({
  accountId: 'example.testnet',
  atMomentOf: { blockHeight: 123456789 }
});

// Query at specific block hash
const result3 = await client.getAccountInfo({
  accountId: 'example.testnet',
  atMomentOf: { blockHash: 'ABC123...' }
});

// Query at optimistic block (not yet finalized)
const result4 = await client.getAccountInfo({
  accountId: 'example.testnet',
  atMomentOf: 'LatestNearFinalBlock'
});

Safe Error Handling

Use the safe variants to handle errors without throwing exceptions:
import { createTestnetClient, isNatError } from 'near-api-ts';

const client = createTestnetClient();

const result = await client.safeGetAccountInfo({
  accountId: 'nonexistent.testnet'
});

if (result.ok) {
  console.log('Account balance:', result.value.accountInfo.balance.total.near);
} else {
  if (isNatError(result.error, 'Client.GetAccountInfo.Rpc.Account.NotFound')) {
    console.log('Account does not exist');
  } else {
    console.error('Error:', result.error.kind);
  }
}

Querying Access Keys

Get All Access Keys

Retrieve all access keys for an account:
const { accountAccessKeys } = await client.getAccountAccessKeys({
  accountId: 'example.testnet',
  atMomentOf: 'LatestFinalBlock'
});

for (const accessKey of accountAccessKeys) {
  console.log('Public Key:', accessKey.publicKey);
  console.log('Nonce:', accessKey.accessKey.nonce);
  
  if (accessKey.accessKey.permission.permissionType === 'FullAccess') {
    console.log('Permission: Full Access');
  } else {
    console.log('Permission: Function Call');
    console.log('Receiver:', accessKey.accessKey.permission.receiverId);
    console.log('Methods:', accessKey.accessKey.permission.methodNames);
  }
}

Get Specific Access Key

const { accountAccessKey, blockHash } = await client.getAccountAccessKey({
  accountId: 'example.testnet',
  publicKey: 'ed25519:5FwoV3MFB94ExfgycBvUQaTbTfgSMPAcfX62bgLBqEPR',
  atMomentOf: 'LatestFinalBlock'
});

console.log('Nonce:', accountAccessKey.nonce);
console.log('Block Hash:', blockHash);

Reading Contract State

Calling View Functions

Call read-only contract functions using callContractReadFunction:
import { createTestnetClient } from 'near-api-ts';

const client = createTestnetClient();

// Call a view function that returns JSON
const result = await client.callContractReadFunction({
  contractAccountId: 'counter.testnet',
  functionName: 'get_count',
  functionArgs: {},
  withStateAt: 'LatestFinalBlock'
});

console.log('Count:', result.result);

Passing Function Arguments

// Call view function with arguments
const result = await client.callContractReadFunction({
  contractAccountId: 'nft.testnet',
  functionName: 'nft_token',
  functionArgs: {
    token_id: '123'
  }
});

if (result.result) {
  console.log('Token owner:', result.result.owner_id);
  console.log('Token metadata:', result.result.metadata);
}

Custom Serialization and Deserialization

For non-JSON contract formats (e.g., Borsh), provide custom serializers:
import { toJsonBytes, fromJsonBytes } from 'near-api-ts';

const result = await client.callContractReadFunction({
  contractAccountId: 'contract.testnet',
  functionName: 'get_data',
  functionArgs: { id: 42 },
  options: {
    // Custom argument serializer
    serializeArgs: ({ functionArgs }) => {
      // Convert snake_case for contract
      return toJsonBytes({ data_id: functionArgs.id });
    },
    // Custom result deserializer
    deserializeResult: ({ resultBytes }) => {
      const decoded = fromJsonBytes(resultBytes);
      // Transform the result
      return {
        id: decoded.data_id,
        value: decoded.data_value
      };
    }
  }
});

console.log('Result:', result.result);

Error Handling for Contract Calls

const result = await client.safeCallContractReadFunction({
  contractAccountId: 'contract.testnet',
  functionName: 'get_item',
  functionArgs: { item_id: 999 }
});

if (!result.ok) {
  if (isNatError(result.error, 'Client.CallContractReadFunction.Rpc.Execution.Failed')) {
    console.log('Contract execution failed');
    console.log('Error message:', result.error.context);
  } else if (isNatError(result.error, 'Client.CallContractReadFunction.Rpc.Account.NotFound')) {
    console.log('Contract account does not exist');
  }
}

Querying Blocks

Get Block Information

const { block } = await client.getBlock({
  blockReference: { blockHeight: 123456789 }
});

console.log('Block Height:', block.header.height);
console.log('Block Hash:', block.header.hash);
console.log('Timestamp:', block.header.timestamp);
console.log('Gas Price:', block.header.gasPrice);
console.log('Total Supply:', block.header.totalSupply);
console.log('Chunks:', block.chunks.length);

Get Latest Finalized Block

const { block } = await client.getBlock({
  blockReference: 'LatestFinalBlock'
});

console.log('Latest finalized block height:', block.header.height);

Get Recent Block Hash

Useful for transaction signing:
const { blockHash } = await client.getRecentBlockHash();
console.log('Recent block hash:', blockHash);

Complete Example: Account Dashboard

Here’s a complete example that creates an account information dashboard:
1

Create the client

import { createTestnetClient, isNatError } from 'near-api-ts';

const client = createTestnetClient();
2

Fetch account information

async function getAccountDashboard(accountId: string) {
  // Get account info
  const accountResult = await client.safeGetAccountInfo({
    accountId,
    atMomentOf: 'LatestFinalBlock'
  });

  if (!accountResult.ok) {
    if (isNatError(accountResult.error, 'Client.GetAccountInfo.Rpc.Account.NotFound')) {
      return { error: 'Account not found' };
    }
    return { error: 'Failed to fetch account info' };
  }

  const { accountInfo } = accountResult.value;
  return accountInfo;
}
3

Fetch access keys

async function getAccountKeys(accountId: string) {
  const result = await client.safeGetAccountAccessKeys({
    accountId,
    atMomentOf: 'LatestFinalBlock'
  });

  if (!result.ok) {
    return [];
  }

  return result.value.accountAccessKeys;
}
4

Combine and display

async function displayDashboard(accountId: string) {
  console.log(`\n=== Dashboard for ${accountId} ===\n`);

  // Get account info
  const info = await getAccountDashboard(accountId);
  if ('error' in info) {
    console.log('Error:', info.error);
    return;
  }

  console.log('Balance:');
  console.log('  Total:', info.balance.total.near, 'NEAR');
  console.log('  Available:', info.balance.available.near, 'NEAR');
  console.log('  Locked:', info.balance.locked.near, 'NEAR');
  console.log('\nStorage:');
  console.log('  Used:', info.storageUsed, 'bytes');
  console.log('  Available:', info.storageAvailable, 'bytes');

  // Get access keys
  const keys = await getAccountKeys(accountId);
  console.log('\nAccess Keys:', keys.length);
  for (const key of keys) {
    console.log('  -', key.publicKey);
    console.log('    Type:', key.accessKey.permission.permissionType);
    console.log('    Nonce:', key.accessKey.permission.nonce);
  }
}

// Run the dashboard
displayDashboard('example.testnet');

Best Practices

Use LatestFinalBlock for Consistency

Always use 'LatestFinalBlock' for production queries to ensure data is finalized:
// Good: finalized data
const result = await client.getAccountInfo({
  accountId: 'account.testnet',
  atMomentOf: 'LatestFinalBlock'
});

// Avoid in production: may be rolled back
const result = await client.getAccountInfo({
  accountId: 'account.testnet',
  atMomentOf: 'LatestNearFinalBlock'
});

Handle Errors Gracefully

Use safe variants for better error handling:
const result = await client.safeGetAccountInfo({ accountId: 'test.near' });

if (!result.ok) {
  // Handle specific error types
  if (isNatError(result.error, 'Client.GetAccountInfo.Rpc.Account.NotFound')) {
    // Account doesn't exist
  } else if (isNatError(result.error, 'Client.GetAccountInfo.Rpc.Block.NotFound')) {
    // Block reference is invalid
  }
}

Cache Block Hashes

The client automatically caches recent block hashes:
// This is cached and efficient
const { blockHash } = await client.getRecentBlockHash();

Next Steps