APIClient

The APIClient provided by the Antelope library is an abstraction built using Fetch that gives developers and their applications easy access to the native API calls.

Usage

Creating an APIClient

In order to create a working client in browser-based environments, the only required parameter is the URL of the API it should use to make requests against. This will also work in a Node.js environment where the version is greater than v18.

import { APIClient } from "@wharfkit/antelope"

const client = new APIClient({
  url: "https://jungle4.greymass.com",
})

Fetch Compatibility

In a Node.js environment where the version is less than v18 and fetch isn’t natively available, an instance of a FetchProvider must be created and given to the APIClient.

This example uses the node-fetch package.

import { APIClient } from "@wharfkit/antelope"
import fetch from "node-fetch"

const provider = new FetchProvider("https://jungle4.greymass.com", {
  fetch,
})
const client = new APIClient({ provider })

Using an APIClient

Once an APIClient is established for a given chain, it will give access to a number of predefined API endpoints as method calls.

The list of available methods embedded in the APIClient can be found in either the ChainAPI or HistoryAPI autodocs. The autocompletion helpers in the developer’s IDE should also prompt with the available options in either client.v1.chain or client.v1.history.

Every API call made through the APIClient returns a promise that must be handled either through await or .then. The example below illustrates the two ways you could call the /v1/chain/get_info API endpoint and return a response.

const response = await client.v1.chain.get_info()

// or ...

client.v1.chain.get_info().then((response) => {
  // your code
})

Typed Responses

The response returned from the APIClient instance will be fully typed using Antelope core types. The above call will return an instance of GetInfoResponse, which automatically typed all of the values to mirror the blockchain state.

GetInfoResponse {
  server_version: '905c5cc9',
  chain_id: Checksum256 {
    array: Uint8Array(32) [
      115, 228,  56,  90,  39,   8, 230, 215,
        4, 136,  52, 251, 193,   7, 159,  47,
      171, 177, 123,  60,  18,  91,  20, 106,
      244,  56, 151,  30, 144, 113, 108,  77
    ]
  },
  head_block_num: UInt32 {
    value: BN { negative: 0, words: [Array], length: 2, red: null }
  },
  last_irreversible_block_num: UInt32 {
    value: BN { negative: 0, words: [Array], length: 2, red: null }
  },
  last_irreversible_block_id: BlockId {
    array: Uint8Array(32) [
        4, 244,  73, 172,  12,  16, 248, 191,
      226,  16, 109, 248,  37, 135,  20,  33,
      213, 168, 237, 166, 185, 122, 106, 142,
       31, 116, 227,  78, 165,  58, 186, 179
    ]
  },
  head_block_id: BlockId {
    array: Uint8Array(32) [
        4, 244,  74, 251, 213, 223,  84,  51,
      198, 171,  96, 238, 195,  71, 100, 253,
      110, 135,  43, 127,  97,  90,   7, 130,
      227, 152, 207,  17,  34,  10, 157, 144
    ]
  },
  head_block_time: TimePoint { value: Int64 { value: [BN] } },
  head_block_producer: Name { value: UInt64 { value: [BN] } },
  virtual_block_cpu_limit: UInt64 {
    value: BN { negative: 0, words: [Array], length: 2, red: null }
  },
  virtual_block_net_limit: UInt64 {
    value: BN { negative: 0, words: [Array], length: 2, red: null }
  },
  block_cpu_limit: UInt64 {
    value: BN { negative: 0, words: [Array], length: 1, red: null }
  },
  block_net_limit: UInt64 {
    value: BN { negative: 0, words: [Array], length: 1, red: null }
  },
  server_version_string: 'v3.1.3',
  fork_db_head_block_num: UInt32 {
    value: BN { negative: 0, words: [Array], length: 2, red: null }
  },
  fork_db_head_block_id: BlockId {
    array: Uint8Array(32) [
        4, 244,  74, 251, 213, 223,  84,  51,
      198, 171,  96, 238, 195,  71, 100, 253,
      110, 135,  43, 127,  97,  90,   7, 130,
      227, 152, 207,  17,  34,  10, 157, 144
    ]
  }
}

Converting Typed Responses

If the application requires the response to be untyped, the Serializer can be used to convert the response into untyped values.

const response = await client.v1.chain.get_info()

const untyped = Serializer.objectify(response)

The resulting response will then use native JavaScript types, as illustrated below.

{
  server_version: '905c5cc9',
  chain_id: '73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d',
  head_block_num: 83119363,
  last_irreversible_block_num: 83119036,
  last_irreversible_block_id: '04f44bbcd76cf399af5e6fb30e6eeab403e2042579387711205c637f82b88c25',
  head_block_id: '04f44d03493c55292840ea3439cf764b9455aae49621c221ccbe9100d028ab23',
  head_block_time: '2023-06-20T22:15:31.000',
  head_block_producer: 'aus1genereos',
  virtual_block_cpu_limit: 200000000,
  virtual_block_net_limit: 1048576000,
  block_cpu_limit: 200000,
  block_net_limit: 1048576,
  server_version_string: 'v3.1.3',
  fork_db_head_block_num: 83119363,
  fork_db_head_block_id: '04f44d03493c55292840ea3439cf764b9455aae49621c221ccbe9100d028ab23'
}

Unsupported API Calls

If an API call isn’t implemented yet within the APIClient, we’d encourage you to contribute to the Antelope codebase and open a pull request.

Contributing New API Calls

An example commit implementing the v1/chain/get_accounts_by_authorizers can be found here for reference.

This commit first defines the call itself as a function with the appropriately typed parameters in the file src/api/v1/chain.ts. It then utilizes the internal client.call function and includes the URL path, required Antelope typed params, and the responseType to indicate which types we expect in the API response.

async get_accounts_by_authorizers(keys: PublicKeyType[]) {
    return this.client.call({
        path: '/v1/chain/get_accounts_by_authorizers',
        params: {keys: keys},
        responseType: AccountsByAuthorizers,
    })
}

The responseType is named AccountsByAuthorizers and is defined in src/api/v1/types.ts. This Struct defines the structure of the data the API will return and defines all the native Antelope types for those fields.

@Struct.type("account_by_authorizers_row")
export class AccountByAuthorizersRow extends Struct {
  @Struct.field(Name) declare account_name: Name
  @Struct.field(Name) declare permission_name: Name
  @Struct.field(PublicKey) declare authorizing_key: PublicKey
  @Struct.field(Weight) declare weight: Weight
  @Struct.field(UInt32) declare threshold: UInt32
}

@Struct.type("account_by_authorizers")
export class AccountsByAuthorizers extends Struct {
  @Struct.field(AccountByAuthorizersRow, { array: true })
  declare accounts: AccountByAuthorizersRow[]
}

With this code implemented, an APIClient instance is able to call:

const response = await client.v1.chain.get_accounts_by_authorizers(
  keys
)

The response will be fully typed and ready to use in a developer’s application.

Manually Calling Undefined API Endpoints

If an endpoint isn’t defined and available for immediate use, the above call structure can be used directly against an APIClient to make ad-hoc calls.

const response = await client.call({
  path: "/v1/chain/get_account",
  params: {
    account_name: "teamgreymass",
  },
})

In this example, it will call the v1/chain/get_account endpoint of the API and return an untyped response. The responseType parameter can also be used in call to automatically type the data.

const response = await client.call({
  path: "/v1/chain/get_account",
  params: {
    account_name: "teamgreymass",
  },
  responseType: AccountObject, // Struct defining the response type
})