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
})