FAQ for NEAR JavaScript API
A collection of Frequently Asked Questions by the community.
Generalβ
Can I use near-api-js
on a static html page?β
You can load the script from a CDN.
<script src="https://cdn.jsdelivr.net/npm/near-api-js@0.45.1/dist/near-api-js.min.js"></script>
Make sure you load the latest version.
Versions list is on npmjs.com
Example Implementation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="messages"></ul>
<textarea id="text" placeholder="Add Message"></textarea>
<button id="add-text">Add Text</button>
<script src="https://cdn.jsdelivr.net/npm/near-api-js@0.45.1/dist/near-api-js.min.js"></script>
<script>
// connect to NEAR
const near = new nearApi.Near({
keyStore: new nearApi.keyStores.BrowserLocalStorageKeyStore(),
networkId: 'testnet',
nodeUrl: 'https://rpc.testnet.near.org',
walletUrl: 'https://testnet.mynearwallet.com/'
});
// connect to the NEAR Wallet
const wallet = new nearApi.WalletConnection(near, 'my-app');
// connect to a NEAR smart contract
const contract = new nearApi.Contract(wallet.account(), 'guest-book.testnet', {
viewMethods: ['getMessages'],
changeMethods: ['addMessage']
});
const button = document.getElementById('add-text');
if (!wallet.isSignedIn()) {
button.textContent = 'SignIn with NEAR'
}
// call the getMessages view method
contract.getMessages()
.then(messages => {
const ul = document.getElementById('messages');
messages.forEach(message => {
const li = document.createElement('li');
li.textContent = `${message.sender} - ${message.text}`;
ul.appendChild(li);
})
});
// Either sign in or call the addMessage change method on button click
document.getElementById('add-text').addEventListener('click', () => {
if (wallet.isSignedIn()) {
contract.addMessage({
args: { text: document.getElementById('text').value },
amount: nearApi.utils.format.parseNearAmount('1')
})
} else {
wallet.requestSignIn({
contractId: 'guest-book.testnet',
methodNames: ['getMessages', 'addMessage']
});
}
});
</script>
</body>
</html>
What front-end frameworks can I use the JavaScript API with?β
The JavaScript API is framework-agnostic. You can include it in any front-end framework, such as React, Vue, Angular, and others.
You can use create-near-app
to quickly bootstrap projects with different templates:
npx create-near-app
Can I use the JavaScript API with mobile JavaScript frameworks such as React Native?β
The JavaScript API can be used in most JavaScript runtimes, and under the hood, itβs an abstraction over NEARβs RPC API. However, notice that the Wallet canβt be used everywhere. For example, in React Native apps youβll be able to use the Wallet in web versions of the apps, but it wonβt work in the native app deployments.
You can use the Wallet in WebView
components in iOS or Android, however be aware that it uses LocalStorage
for KeyStore
, and itβs your responsibility to persist the storage when you manage loading of WebView
components.
Transactionsβ
How to check the status of transactionβ
Please refer to examples about transactions in the Cookbook.
How transactions are signed and sent by near-api-jsβ
There are a few steps involved before transaction data is communicated to the network and eventually included in a block. The following steps are taken when creating, signing and ultimately a transaction from a user's account:
- The user creates a transaction object using the
account.signAndSendTransaction
method. This method accepts an array of actions and returns an object for the outcome of the transaction. - The transaction is signed using the
account.signTransaction
method. This method accepts an array of actions and returns a signed transaction object. - The signed transaction object is sent to the network using the
account.connection.provider.sendTransaction
method. This method accepts a signed transaction object and returns a transaction hash. This step performs the borsh serialization of the transaction object and calls thebroadcast_tx_commit
JSON RPC method with the serialized transaction object encoded in base64.
How to send batch transactionsβ
You may batch send transactions by using the signAndSendTransaction({})
method from account
. This method takes an array of transaction actions, and if one fails, the entire operation will fail. Here's a simple example:
const { connect, transactions, keyStores } = require("near-api-js");
const fs = require("fs");
const path = require("path");
const homedir = require("os").homedir();
const CREDENTIALS_DIR = ".near-credentials";
const CONTRACT_NAME = "spf.idea404.testnet";
const WASM_PATH = path.join(__dirname, "../build/uninitialized_nft.wasm");
const credentialsPath = path.join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);
const config = {
keyStore,
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
sendTransactions();
async function sendTransactions() {
const near = await connect({ ...config, keyStore });
const account = await near.account(CONTRACT_NAME);
const args = { some_field: 1, another_field: "hello" };
const balanceBefore = await account.getAccountBalance();
console.log("Balance before:", balanceBefore);
try {
const result = await account.signAndSendTransaction({
receiverId: CONTRACT_NAME,
actions: [
transactions.deployContract(fs.readFileSync(WASM_PATH)), // Contract does not get deployed
transactions.functionCall("new", Buffer.from(JSON.stringify(args)), 10000000000000, "0"), // this call fails
transactions.transfer("1" + "0".repeat(24)), // 1 NEAR is not transferred either
],
});
console.log(result);
} catch (e) {
console.log("Error:", e);
}
const balanceAfter = await account.getAccountBalance();
console.log("Balance after:", balanceAfter);
}
Balance before: {
total: '49987878054959838200000000',
stateStaked: '4555390000000000000000000',
staked: '0',
available: '45432488054959838200000000'
}
Receipts: 2PPueY6gnA4YmmQUzc8DytNBp4PUpgTDhmEjRSHHVHBd, 3isLCW9SBH1MrPjeEPAmG9saHLj9Z2g7HxzfBdHmaSaG
Failure [spf.idea404.testnet]: Error: {"index":1,"kind":{"ExecutionError":"Smart contract panicked: panicked at 'Failed to deserialize input from JSON.: Error(\"missing field `owner_id`\", line: 1, column: 40)', nft/src/lib.rs:47:1"}}
Error: ServerTransactionError: {"index":1,"kind":{"ExecutionError":"Smart contract panicked: panicked at 'Failed to deserialize input from JSON.: Error(\"missing field `owner_id`\", line: 1, column: 40)', nft/src/lib.rs:47:1"}}
at parseResultError (/Users/dennis/Code/naj-test/node_modules/near-api-js/lib/utils/rpc_errors.js:31:29)
at Account.<anonymous> (/Users/dennis/Code/naj-test/node_modules/near-api-js/lib/account.js:156:61)
at Generator.next (<anonymous>)
at fulfilled (/Users/dennis/Code/naj-test/node_modules/near-api-js/lib/account.js:5:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
type: 'FunctionCallError',
context: undefined,
index: 1,
kind: {
ExecutionError: 'Smart contract panicked: panicked at \'Failed to deserialize input from JSON.: Error("missing field `owner_id`", line: 1, column: 40)\', nft/src/lib.rs:47:1'
},
transaction_outcome: {
block_hash: '5SUhYcXjXR1svCxL5BhCuw88XNdEjKXqWgA9X4XZW1dW',
id: 'SKQqAgnSN27fyHpncaX3fCUxWknBrMtxxytWLRDQfT3',
outcome: {
executor_id: 'spf.idea404.testnet',
gas_burnt: 4839199843770,
logs: [],
metadata: [Object],
receipt_ids: [Array],
status: [Object],
tokens_burnt: '483919984377000000000'
},
proof: [ [Object], [Object], [Object], [Object], [Object] ]
}
}
Balance after: {
total: '49985119959346682700000000',
stateStaked: '4555390000000000000000000',
staked: '0',
available: '45429729959346682700000000'
}
You may also find an example of batch transactions in the Cookbook.
Accountsβ
Whatβs the difference between Account
and ConnectedWalletAccount
?β
Interaction with the wallet is only possible in a web-browser environment because NEARβs Wallet is web-based.
The difference between Account
and ConnectedWalletAccount
is mostly about the way it signs transactions. The ConnectedWalletAccount
uses the wallet to approve transactions.
Under the hood the ConnectedWalletAccount
inherits and overrides some methods of Account
.
How to create implicit accounts?β
You can read about it in the article about Implicit Accounts.
Contractsβ
How do I attach gas / a deposit?β
After contract is instantiated you can then call the contract and specify the amount of attached gas.
await contract.method_name(
{
arg_name: "value", // argument name and value - pass empty object if no args required
},
"300000000000000", // attached GAS (optional)
"1000000000000000000000000" // attached deposit in yoctoNEAR (optional)
);
Common Errorsβ
RPC Errorsβ
Refer to the exhaustive list of error messages that RPC endpoints throws and JavaScript API propagates.
Missing contract methodsβ
When constructing a Contract
instance on the client-side, you need to specify
the contract's methods. If you misspell, mismatch, or miss method names - you'll
receive errors about missing methods.
There are a few cases of missing or wrong methods:
- When you call a method you didn't specify in the constructor.
- When you call a method that doesn't exist on the blockchain's contract (but you did specify it in the client-side constructor).
- When you mismatch between
viewMethods
andchangeMethods
.
For example, let's look at the following contract code.
It contains one view
and one call
method:
@NearBindgen
class MyContract extends NearContract {
constructor() { super(); }
@view
method_A_view(): string {
return 'Hi';
}
@call
method_B_call(): void {}
}
Client-side errors for missing methodsβ
TypeError: contract.METHOD_NAME is not a function
β
The following contract constructor declares only method_A_view
, it doesn't declare method_B_call
const contract = await new nearAPI.Contract(
walletConnection.account(), 'guest-book.testnet',
{
viewMethods: ['method_A_view'], // <=== Notice this
changeMethods: [], // <=== Notice this
sender: walletConnection.getAccountId(),
}
);
// This will work because we declared `method_A_view` in constructor
await contract.method_A_view();
// This will throw `TypeError: contract.method_B_call is not a function`
// because we didn't declare `method_B_call` in constructor,
// even if it exists in the real contract.
await contract.method_B_call();
// This will also throw `TypeError: contract.method_C is not a function`,
// not because `method_C` doesn't exist on the contract, but because we didn't declare it
// in the client-side constructor.
await contract.method_C();
RPC errors for missing methodsβ
wasm execution failed with error: FunctionCallError(MethodResolveError(MethodNotFound))
β
In this example we specify and call a method, but this method doesn't exist on the blockchain:
const contract = await new nearAPI.Contract(
// ...
{
viewMethods: ["method_C"], // <=== method_C doesn't exist on the contract above
changeMethods: [],
// ...
}
);
// We did specify `method_C` name in constructor, so this function exists on client-side `contract` instance,
// but a method with this name does not exist on the actual contract on the blockchain.
// This will return an error from RPC call `FunctionCallError(MethodResolveError(MethodNotFound))`
// and will throw it on the client-side
await contract.method_C();
// Notice: if we call `method_A_view` we get `TypeError: contract.method_A_view is not a function`.
// Even though the method exists on the actual blockchain contract,
// we didn't specify `method_A_view` in the contract's client-side constructor.
await contract.method_A_view();
wasm execution failed with error: FunctionCallError(HostError(ProhibitedInView { method_name: "storage_write" }))
β
Last case is when you mismatch viewMethods
and changeMethods
.
In the contract above we declared:
- A
@view
method namedmethod_A_view
- A
@call
method namedmethod_B_call
In a client-side constructor, the contract's @view
method names must be specified under viewMethods
,
and the contract's @call
method names must be specified under changeMethods
.
If we mismatch between the types we will receive errors.
For example:
const contract = await new nearAPI.Contract(
// ...
{
viewMethods: ['method_B_call'], // <=== here should be `method_A_view`
changeMethods: ['method_A_view'], // <=== and here should be `method_B_call`
// ...
}
);
// This will return an error from RPC call and throw:
// `wasm execution failed with error: FunctionCallError(HostError(ProhibitedInView { method_name: "storage_write" }))`
// This error indicates that we are trying to call a state-changing method but declare it as a read-only method in client-side.
await contract.method_B_call();
// The following behavior is undefined and might not work as expected.
// `method_A_veiw` should be declared under `viewMethods` and in our example here we declare it under `changeMethods`.
await contract.method_A_view();
Class {X}
is missing in schema: publicKeyβ
There is currently a known issue with the JavaScript API library, when you import
it more than once
it might cause a namespace collision.
A temporary workaround: make sure you don't re-import it, for example when running tests.
regeneratorRuntime
is not definedβ
You are probably using Parcel like we do in other examples. Please make sure you have this line at the top of your main JS file. (Most likely index.js
):
import "regenerator-runtime/runtime";
- Also, ensure the dependencies for this are added to the project by checking the dev dependencies in your
package.json
. If not found you can install this by running the following in your terminal:
npm install regenerator-runtime --save-dev
Window error using Node.js
β
You're maybe using a KeyStore that's for the browser. Instead, use a filesystem key or private key string.
Browser KeyStore:
const { keyStores } = require("near-api-js");
const keyStore = new keyStores.BrowserLocalStorageKeyStore();
FileSystem KeyStore:
const { keyStores } = require("near-api-js");
const KEY_PATH = "~./near-credentials/testnet/example-account.json";
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(KEY_PATH);