Indexer lookups / searching is a higher-order use case capability provided by AlgoKit Utils that builds on top of the core capabilities. It provides type-safe indexer API wrappers (no more Record<string, any>
pain), including automatic pagination control.
To see some usage examples check out the automated tests.
To import the indexer functions you can:
import { indexer } from '@algorandfoundation/algokit-utils'
All of the indexer functions require you to pass in an indexer SDK client, which you can get from AlgorandClient
via algorand.client.indexer
. These calls are not made more easy to call by exposing via AlgorandClient
and thus not requiring the indexer SDK client to be passed in. This is because we want to add a tiny bit of friction to using indexer, given it's an expensive API to run for node providers, the data from it can sometimes be slow and stale, and there are alternatives that allow individual projects to index subsets of chain data specific to them as a preferred option. In saying that, it's a very useful API for doing ad hoc data retrieval, writing automated tests, and many other uses.
There is a subset of indexer API calls that are exposed as easy to use methods with correct typing exposed and automatic pagination for multi item returns.
indexer.lookupTransactionById(transactionId, algorand.client.indexer)
- Finds a transaction by IDindexer.lookupAccountByAddress(accountAddress, algorand.client.indexer)
- Finds an account by addressindexer.lookupAccountCreatedApplicationByAddress(algorand.client.indexer, address, getAll?, paginationLimit?)
- Finds all applications created for an accountindexer.lookupAssetHoldings(algorand.client.indexer, assetId, options?, paginationLimit?)
- Finds all asset holdings for the given assetindexer.searchTransactions(algorand.client.indexer, searchCriteria, paginationLimit?)
- Search for transactions with a given set of criteriaindexer.executePaginatedRequest(extractItems, buildRequest)
- Execute the given indexer request with automatic pagination
To use the indexer.searchTransaction
method, you can follow this example as a starting point:
const transactions = await indexer.searchTransactions(algorand.client.indexer, (s) =>
s.txType('pay').addressRole('sender').address(myAddress),
)
To use the indexer.executePaginatedRequest
method, you can follow this example as a starting point:
const transactions = await executePaginatedRequest(
(response: TransactionSearchResults) => {
return response.transactions
},
(nextToken) => {
let s = algorand.client.indexer.searchForTransactions().txType('pay').address(myAddress).limit(1000)
if (nextToken) {
s = s.nextToken(nextToken)
}
return s
},
)
It takes the first lambda to translate the raw response into the array that should keep getting appended as the pagination is followed and the second lambda constructs the request (without the .do()
call), including populating the pagination token.
The response model type definitions for the majority of indexer API are exposed from the types/indexer
namespace in AlgoKit Utils. This is so that you can have a much better experience than the default response type of Record<string, any>
from the indexer client in algosdk
. If there is a type you want to use that is missing feel free to submit a pull request to add the type(s).
To access these types you can import them:
import { /* ... */ } '@algorandfoundation/algokit-utils/types/indexer'
As a general convention, the response types are named {TypeName}Result
for a single item result and {TypeName}Results
for a multiple item result where {TypeName}
is:
{Entity}Lookup
for an API call response that returns a lookup for a single entity e.g.AssetLookupResult
{Entity}Search
for an API call response that searches for a type of entity e.g.TransactionSearchResults
- The
UpperCamelCase
name of a given model type as specified in the official documentation for any sub-types within a response e.g.ApplicationResult
The reason Result/Results
is suffixed to the type is to avoid type name clashes for commonly used types from algosdk
like Transaction
.
To use these types with an indexer call you simply need to find the right result type and cast the response from .do()
for the call in question, e.g.:
import { TransactionLookupResult } from '@algorandfoundation/algokit-utils/types/indexer'
...
const transaction = (await algorand.client.indexer.lookupTransactionByID(transactionId).do()) as TransactionLookupResult