Using BlockReq RPC with viem
viem separates chain metadata from the transport that talks to JSON-RPC. That split is useful with BlockReq because you can make endpoint selection explicit and keep the client type-safe.
pnpm add viem
export BLOCKREQ_ETH_RPC_URL="https://ethereum-rpc.blockreq.com/v1/rpc/public"
export BLOCKREQ_BASE_RPC_URL="https://base-rpc.blockreq.com/v1/rpc/public"
Start with a public client for read-only JSON-RPC calls. Match the chain value to the BlockReq endpoint you pass into http.
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
const rpcUrl = process.env.BLOCKREQ_ETH_RPC_URL;
if (!rpcUrl) {
throw new Error("BLOCKREQ_ETH_RPC_URL is required");
}
export const ethereumClient = createPublicClient({
chain: mainnet,
transport: http(rpcUrl, {
timeout: 10_000,
}),
});
const chainId = await ethereumClient.getChainId();
const blockNumber = await ethereumClient.getBlockNumber();
console.log({ chainId, blockNumber });
Use a second client for Base instead of swapping only the URL. This makes the chain id expectation obvious in code review.
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";
const rpcUrl = process.env.BLOCKREQ_BASE_RPC_URL;
if (!rpcUrl) {
throw new Error("BLOCKREQ_BASE_RPC_URL is required");
}
export const baseClient = createPublicClient({
chain: base,
transport: http(rpcUrl),
});
Contract reads
For contract reads, keep ABIs small and colocated with the code path that needs them. The example below reads ERC-20 metadata through a normal eth_call.
const erc20Abi = [
{
type: "function",
name: "decimals",
stateMutability: "view",
inputs: [],
outputs: [{ type: "uint8" }],
},
] as const;
const decimals = await ethereumClient.readContract({
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
abi: erc20Abi,
functionName: "decimals",
});
console.log({ decimals });
Production guardrails
Check getChainId during startup or health checks when a service is configured dynamically. If you use WebSocket subscriptions, use a WSS endpoint that is meant for subscriptions and verify the route supports the subscription method you need. Do not assume HTTP endpoints support subscription behavior.