Ethereum

Ethereum

JavaScript / TypeScript

JavaScript has two mature libraries for talking to Ethereum: web3.js, the original, and ethers.js, the leaner modern option. (viem is a third, increasingly popular choice, but the examples here cover the two classics.) Both are actively maintained and both speak the same Ethereum JSON-RPC under the hood. For a new project, start with ethers.js v6 — its TypeScript types are written by hand rather than generated, the tree-shakeable build keeps a browser bundle small, and the BrowserProvider / JsonRpcProvider split maps cleanly onto MetaMask versus a backend connection to chain 1.

Both web3.js and ethers.js are under active development. For new Ethereum work, ethers.js v6 is the safer default — first-class TypeScript types and a noticeably smaller bundle when shipping to the browser.

Web3.js

web3.js is the original Ethereum JavaScript API and still the one most tutorials and older dApps are written against. Install it with npm install web3, then construct new Web3('https://ethereum.therpc.io/YOUR_API_KEY') — passing the URL string lets web3.js build its own HTTP provider, so a balance read is just web3.eth.getBalance(address) returning wei that web3.utils.fromWei turns into ETH.

import Web3 from 'web3';
const web3 = new Web3('https://ethereum.therpc.io/YOUR_API_KEY');
// Get balance
const getBalance = async (address) => {
const balance = await web3.eth.getBalance(address);
const balanceInEth = web3.utils.fromWei(balance, 'ether');
return balanceInEth;
};
// Send transaction
const sendTransaction = async (from, to, value) => {
const tx = {
from,
to,
value: web3.utils.toWei(value, 'ether'),
};
return await web3.eth.sendTransaction(tx);
};
  • GitHub: https://github.com/web3/web3.js
  • Docs: https://web3js.readthedocs.io/
  • Complete coverage of the Ethereum JSON-RPC API
  • WebSocket transport for eth_subscribe (newHeads, logs, pending txs)
  • Contract interaction from ABIs, including ERC-20 and ERC-721
  • ENS resolution for .eth names
  • One of the largest communities and the most third-party examples

Ethers.js

ethers.js takes a more compact, modular approach and ships TypeScript types as part of the core, not a separate package. Install with npm install ethers. For a server or script, wrap the endpoint in a JsonRpcProvider: new ethers.JsonRpcProvider('https://ethereum.therpc.io/YOUR_API_KEY'). From there provider.getBalance(address) returns a bigint of wei and ethers.formatEther renders it as a human ETH string — no decimal juggling.

import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://ethereum.therpc.io/YOUR_API_KEY');
// Get balance
const getBalance = async (address) => {
const balance = await provider.getBalance(address);
return ethers.formatEther(balance);
};
// Send transaction
const sendTransaction = async (wallet, to, value) => {
const tx = await wallet.sendTransaction({
to,
value: ethers.parseEther(value),
});
return await tx.wait();
};
  • GitHub: https://github.com/ethers-io/ethers.js/
  • Docs: https://docs.ethers.org/
  • Hand-written TypeScript types shipped in the core package
  • Smaller, tree-shakeable bundle for browser dApps
  • Careful key handling and a strict, predictable API surface
  • ENS resolution built in
  • A large, well-maintained test suite tracking Ethereum behavior

TypeScript Support

Neither library needs a @types/* companion — both bundle their declarations, so a fresh npm install gives you typed access to Ethereum out of the box. In practice that means your editor autocompletes provider.getBalance and flags a wrong argument before you run anything, while the types double as inline docs for which RPC methods exist and what they return. The snippet below types a transaction shape so a missing to or a string where a bigint belongs fails at compile time, not on mainnet.

import { BigNumber } from 'ethers';
interface TransactionData {
to: string;
value: BigNumber;
gasLimit?: BigNumber;
}
const createTransaction = async (data: TransactionData) => {
// Your implementation
};

Node.js Usage

On the server, both libraries run the same way against the Ethereum endpoint regardless of module system. Use require() if your project is CommonJS or import if it's ESM — the connection code is identical, only the loading syntax differs. This is the setup you'd use for an indexer, a webhook that watches Ethereum logs, or any backend that signs transactions away from the browser.

// Web3.js in Node.js
const Web3 = require('web3');
const web3 = new Web3('https://ethereum.therpc.io/YOUR_API_KEY');
// Ethers.js in Node.js
const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider('https://ethereum.therpc.io/YOUR_API_KEY');

Browser Usage

In the browser, import either library as an ES module and let a bundler like Vite or webpack package it for the page. There's a key distinction here: use a JsonRpcProvider pointed at the endpoint for read-only data, but when you need the user to sign — connect their wallet, send ETH, approve a token — go through new ethers.BrowserProvider(window.ethereum). That hands signing to MetaMask or another injected wallet, so the private key stays in the extension and your dApp only ever sees the signed result.

// Using ES modules
import { Web3 } from 'web3';
// or
import { ethers } from 'ethers';
// Using window.ethereum (MetaMask)
const provider = new ethers.BrowserProvider(window.ethereum);

Ready to call this in production?

Free tier covers personal projects. Pay-as-you-go scales without a card.