DEV Community

Cover image for Building a Production-Ready Blockchain API with Node.js and Web3.js
Flavio Espinoza
Flavio Espinoza

Posted on

Building a Production-Ready Blockchain API with Node.js and Web3.js

Learn how to create a professional REST API that fetches real-time cryptocurrency prices from Chainlink oracles and analyzes ERC-20 tokens on Ethereum


πŸš€ Introduction

Ever wondered how DeFi applications get real-time cryptocurrency prices? Or how they interact with thousands of different tokens seamlessly? The answer lies in smart contract APIs that connect traditional web applications to blockchain data.

In this tutorial, I'll show you how to build a production-ready REST API that:

  • πŸ“Š Fetches live ETH and BTC prices from Chainlink oracles
  • πŸͺ™ Analyzes any ERC-20 token on Ethereum
  • πŸ” Provides network diagnostics and health monitoring
  • ⚑ Handles errors gracefully with proper validation

By the end, you'll have a professional blockchain API that demonstrates real-world Web3 development skills.

🎯 What We're Building

Our API will have these endpoints:

GET /api                           # API information
GET /api/test/network             # Network verification  
GET /api/token/:address           # ERC-20 token details
GET /api/chainlink/price/eth      # Live ETH/USD price
GET /api/chainlink/price/btc      # Live BTC/USD price
Enter fullscreen mode Exit fullscreen mode

Tech Stack:

  • Backend: Node.js + Express.js
  • Blockchain: Web3.js for Ethereum interaction
  • Data Sources: Chainlink Price Feeds + ERC-20 contracts
  • Network: Ethereum Mainnet

πŸ› οΈ Prerequisites

Before we start, make sure you have:

  • Node.js (v16+) installed
  • Basic understanding of JavaScript and APIs
  • Familiarity with blockchain concepts (helpful but not required)
  • A code editor (VS Code recommended)

Don't worry if you're new to Web3 - I'll explain everything step by step!

πŸ“¦ Project Setup

Let's create our project and install dependencies:

# Create project directory
mkdir blockchain-api
cd blockchain-api

# Initialize package.json
yarn init -y

# Install dependencies
yarn add express web3 dotenv

# Install development dependencies  
yarn add -D nodemon

# Create project structure
touch index.js .env .gitignore
Enter fullscreen mode Exit fullscreen mode

Update your package.json scripts:

{
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

Create .gitignore:

node_modules/
.env
.DS_Store
Enter fullscreen mode Exit fullscreen mode

🌐 Environment Configuration

Create your .env file:

# .env
WEB3_RPC_URL=https://eth.llamarpc.com
PORT=3000
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Pro Tip: We're using a free RPC endpoint, but for production, consider Alchemy or Infura for better reliability and rate limits.

πŸ’» Code Implementation

Now let's build our API! Create index.js:

require('dotenv').config();
const express = require('express');
const { Web3 } = require('web3');

const app = express();
const port = process.env.PORT || 3000;

// Initialize Web3 with RPC endpoint
const web3 = new Web3(process.env.WEB3_RPC_URL);

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// CORS middleware
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  next();
});

// ERC-20 Token ABI (standard interface)
const ERC20_ABI = [
  {
    "constant": true,
    "inputs": [],
    "name": "name",
    "outputs": [{"name": "", "type": "string"}],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "symbol",
    "outputs": [{"name": "", "type": "string"}],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "decimals",
    "outputs": [{"name": "", "type": "uint8"}],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "totalSupply",
    "outputs": [{"name": "", "type": "uint256"}],
    "type": "function"
  }
];

// Chainlink Price Feed ABI
const CHAINLINK_PRICE_FEED_ABI = [
  {
    "inputs": [],
    "name": "latestRoundData",
    "outputs": [
      {"internalType": "uint80", "name": "roundId", "type": "uint80"},
      {"internalType": "int256", "name": "answer", "type": "int256"},
      {"internalType": "uint256", "name": "startedAt", "type": "uint256"},
      {"internalType": "uint256", "name": "updatedAt", "type": "uint256"},
      {"internalType": "uint80", "name": "answeredInRound", "type": "uint80"}
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "description",
    "outputs": [{"internalType": "string", "name": "", "type": "string"}],
    "stateMutability": "view",
    "type": "function"
  }
];

// Smart contract addresses on Ethereum Mainnet
const CONTRACTS = {
  USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
  DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
  CHAINLINK_ETH_USD: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
  CHAINLINK_BTC_USD: '0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c'
};
Enter fullscreen mode Exit fullscreen mode

🏠 Main API Endpoint

// API Information endpoint
app.get('/api', (req, res) => {
  res.json({
    message: 'Blockchain Smart Contract API',
    version: '1.0.0',
    endpoints: [
      'GET /api/token/:address - Get ERC-20 token information',
      'GET /api/chainlink/price/eth - Get ETH/USD price from Chainlink',
      'GET /api/chainlink/price/btc - Get BTC/USD price from Chainlink',
      'GET /api/test/network - Get network information'
    ]
  });
});
Enter fullscreen mode Exit fullscreen mode

πŸ” Network Verification

// Network diagnostics endpoint
app.get('/api/test/network', async (req, res) => {
  try {
    const blockNumber = await web3.eth.getBlockNumber();
    const chainId = await web3.eth.getChainId();
    const gasPrice = await web3.eth.getGasPrice();

    // Verify we're on Ethereum Mainnet
    const networkName = Number(chainId) === 1 ? 'Ethereum Mainnet' : `Unknown (Chain ID: ${chainId})`;

    // Check contract existence
    const ethContractCode = await web3.eth.getCode(CONTRACTS.CHAINLINK_ETH_USD);
    const usdcContractCode = await web3.eth.getCode(CONTRACTS.USDC);

    res.json({
      network: {
        chainId: chainId.toString(),
        name: networkName,
        blockNumber: blockNumber.toString(),
        gasPrice: gasPrice.toString()
      },
      contracts: {
        chainlinkEthUsd: {
          address: CONTRACTS.CHAINLINK_ETH_USD,
          hasCode: ethContractCode.length > 2
        },
        usdc: {
          address: CONTRACTS.USDC,
          hasCode: usdcContractCode.length > 2
        }
      }
    });
  } catch (error) {
    res.status(500).json({
      error: 'Network verification failed',
      details: error.message
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

πŸͺ™ ERC-20 Token Analysis

// Get ERC-20 token information
app.get('/api/token/:address', async (req, res) => {
  try {
    const { address } = req.params;

    // Validate Ethereum address format
    if (!web3.utils.isAddress(address)) {
      return res.status(400).json({ 
        error: 'Invalid Ethereum address format' 
      });
    }

    // Create contract instance
    const contract = new web3.eth.Contract(ERC20_ABI, address);

    // Fetch token data in parallel for better performance
    const [name, symbol, decimals, totalSupply] = await Promise.all([
      contract.methods.name().call(),
      contract.methods.symbol().call(),
      contract.methods.decimals().call(),
      contract.methods.totalSupply().call()
    ]);

    res.json({
      address,
      name,
      symbol,
      decimals: decimals.toString(),
      totalSupply: totalSupply.toString(),
      // Convert to human-readable format
      totalSupplyFormatted: (Number(totalSupply) / Math.pow(10, Number(decimals))).toLocaleString()
    });
  } catch (error) {
    res.status(500).json({
      error: 'Failed to fetch token information',
      details: error.message
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

πŸ’° Chainlink Price Feeds

Here's where it gets exciting - real-time price data!

// Get ETH/USD price from Chainlink oracle
app.get('/api/chainlink/price/eth', async (req, res) => {
  try {
    const contract = new web3.eth.Contract(
      CHAINLINK_PRICE_FEED_ABI,
      CONTRACTS.CHAINLINK_ETH_USD
    );

    // Fetch latest price data
    const [roundData, decimals, description] = await Promise.all([
      contract.methods.latestRoundData().call(),
      contract.methods.decimals().call(),
      contract.methods.description().call()
    ]);

    // Convert raw price to human-readable format
    const price = Number(roundData.answer) / Math.pow(10, Number(decimals));
    const lastUpdated = new Date(Number(roundData.updatedAt) * 1000);

    res.json({
      pair: 'ETH/USD',
      price: price.toFixed(2),
      rawPrice: roundData.answer.toString(),
      decimals: decimals.toString(),
      description,
      lastUpdated: lastUpdated.toISOString(),
      source: 'Chainlink Decentralized Oracle Network'
    });
  } catch (error) {
    res.status(500).json({
      error: 'Failed to fetch ETH/USD price',
      details: error.message
    });
  }
});

// Get BTC/USD price from Chainlink oracle
app.get('/api/chainlink/price/btc', async (req, res) => {
  try {
    const contract = new web3.eth.Contract(
      CHAINLINK_PRICE_FEED_ABI,
      CONTRACTS.CHAINLINK_BTC_USD
    );

    const [roundData, decimals, description] = await Promise.all([
      contract.methods.latestRoundData().call(),
      contract.methods.decimals().call(),
      contract.methods.description().call()
    ]);

    const price = Number(roundData.answer) / Math.pow(10, Number(decimals));
    const lastUpdated = new Date(Number(roundData.updatedAt) * 1000);

    res.json({
      pair: 'BTC/USD',
      price: price.toFixed(2),
      rawPrice: roundData.answer.toString(),
      decimals: decimals.toString(),
      description,
      lastUpdated: lastUpdated.toISOString(),
      source: 'Chainlink Decentralized Oracle Network'
    });
  } catch (error) {
    res.status(500).json({
      error: 'Failed to fetch BTC/USD price',
      details: error.message
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

πŸš€ Server Startup

// Start server with connection verification
app.listen(port, () => {
  console.log(`πŸš€ Blockchain API running on port ${port}`);
  console.log(`πŸ“ Endpoints available at http://localhost:${port}/api`);

  // Test Web3 connection on startup
  web3.eth.getBlockNumber()
    .then(blockNumber => {
      console.log(`βœ… Connected to Ethereum! Latest block: ${blockNumber}`);
    })
    .catch(error => {
      console.error('❌ Web3 connection failed:', error.message);
    });
});
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Testing Your API

Start your server:

yarn dev
Enter fullscreen mode Exit fullscreen mode

You should see:

πŸš€ Blockchain API running on port 3000
πŸ“ Endpoints available at http://localhost:3000/api
βœ… Connected to Ethereum! Latest block: 19785697
Enter fullscreen mode Exit fullscreen mode

Test the endpoints:

1. API Information:

curl http://localhost:3000/api
Enter fullscreen mode Exit fullscreen mode

2. Network Verification:

curl http://localhost:3000/api/test/network
Enter fullscreen mode Exit fullscreen mode

3. Real-time ETH Price:

curl http://localhost:3000/api/chainlink/price/eth
Enter fullscreen mode Exit fullscreen mode

4. USDC Token Analysis:

curl http://localhost:3000/api/token/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Enter fullscreen mode Exit fullscreen mode

πŸ”§ Understanding the Magic

Chainlink Oracles: Decentralized Price Feeds

Instead of relying on a single API that could fail or be manipulated, Chainlink aggregates price data from multiple independent nodes and data sources. This creates tamper-resistant, highly available price feeds that major DeFi protocols trust with billions of dollars.

ERC-20 Token Standard

Every ERC-20 token implements the same interface, which means our API can analyze any of the thousands of tokens on Ethereum using the same code. Pretty powerful!

Web3.js vs Traditional APIs

Unlike REST APIs that talk to centralized servers, Web3.js connects directly to the Ethereum blockchain. This means:

  • βœ… Censorship-resistant - No single point of failure
  • βœ… Transparent - All data is verifiable on-chain
  • βœ… Global - Same data available worldwide
  • ❌ Slower - Blockchain calls take more time than database queries

🚨 Production Considerations

Before deploying to production, consider:

Rate Limiting & Caching

// Add rate limiting
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

app.use('/api', limiter);
Enter fullscreen mode Exit fullscreen mode

Better RPC Providers

Free RPC endpoints have rate limits. For production:

  • Alchemy - 300M compute units/month free
  • Infura - 100k requests/day free
  • QuickNode - High performance, paid plans

Error Handling & Monitoring

// Add comprehensive error logging
const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.Console()
  ]
});
Enter fullscreen mode Exit fullscreen mode

Input Validation

// Add request validation
const { body, validationResult } = require('express-validator');

app.get('/api/token/:address', [
  param('address').isEthereumAddress().withMessage('Invalid Ethereum address')
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  // ... rest of endpoint logic
});
Enter fullscreen mode Exit fullscreen mode

🎯 What's Next?

Now that you have a working blockchain API, here are some ideas to expand it:

πŸ”₯ Advanced Features:

  • Token balances - Check how much of any token an address holds
  • DEX price data - Get prices from Uniswap, SushiSwap, etc.
  • NFT metadata - Analyze ERC-721 and ERC-1155 tokens
  • DeFi protocols - Integrate with Aave, Compound, Yearn
  • WebSocket support - Real-time price streaming
  • Historical data - Price charts and analytics

πŸ›‘οΈ Enterprise Features:

  • Authentication - API keys and JWT tokens
  • Database integration - Cache data for better performance
  • Multi-chain support - Polygon, BSC, Arbitrum, Optimism
  • Load balancing - Handle high traffic
  • CI/CD pipeline - Automated testing and deployment

πŸ’‘ Key Takeaways

Building blockchain APIs opens up incredible possibilities:

  1. Real-time financial data without paying for expensive traditional APIs
  2. Global, censorship-resistant applications
  3. Composable services - build on top of existing DeFi protocols
  4. Transparent, verifiable data sources

The Web3 ecosystem is still early, which means there's huge opportunity for developers who understand how to bridge traditional web development with blockchain technology.

πŸ”— Resources

πŸš€ Deploy Your API

Ready to show the world? Deploy to:


What will you build next? Drop a comment with your ideas, and don't forget to ⭐ this article if it helped you get started with blockchain development!

Follow me for more Web3 development tutorials and blockchain deep dives! 🌐


πŸ’» Complete Code

Here's the complete index.js file for easy copy-paste:

require('dotenv').config();
const express = require('express');
const { Web3 } = require('web3');

const app = express();
const port = process.env.PORT || 3000;

// Initialize Web3 with RPC endpoint
const web3 = new Web3(process.env.WEB3_RPC_URL);

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// CORS middleware
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  next();
});

// ERC-20 Token ABI (standard interface)
const ERC20_ABI = [
  {
    "constant": true,
    "inputs": [],
    "name": "name",
    "outputs": [{"name": "", "type": "string"}],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "symbol",
    "outputs": [{"name": "", "type": "string"}],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "decimals",
    "outputs": [{"name": "", "type": "uint8"}],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "totalSupply",
    "outputs": [{"name": "", "type": "uint256"}],
    "type": "function"
  }
];

// Chainlink Price Feed ABI
const CHAINLINK_PRICE_FEED_ABI = [
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "description",
    "outputs": [{"internalType": "string", "name": "", "type": "string"}],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "latestAnswer",
    "outputs": [{"internalType": "int256", "name": "", "type": "int256"}],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "latestRoundData",
    "outputs": [
      {"internalType": "uint80", "name": "roundId", "type": "uint80"},
      {"internalType": "int256", "name": "answer", "type": "int256"},
      {"internalType": "uint256", "name": "startedAt", "type": "uint256"},
      {"internalType": "uint256", "name": "updatedAt", "type": "uint256"},
      {"internalType": "uint80", "name": "answeredInRound", "type": "uint80"}
    ],
    "stateMutability": "view",
    "type": "function"
  }
];

// Smart contract addresses on Ethereum Mainnet
const CONTRACTS = {
  USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
  DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
  CHAINLINK_ETH_USD: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
  CHAINLINK_BTC_USD: '0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c'
};

// API Information endpoint
app.get('/api', (req, res) => {
  res.json({
    message: 'Blockchain Smart Contract API',
    version: '1.0.0',
    endpoints: [
      'GET /api/token/:address - Get ERC-20 token information',
      'GET /api/chainlink/price/eth - Get ETH/USD price from Chainlink',
      'GET /api/chainlink/price/btc - Get BTC/USD price from Chainlink',
      'GET /api/test/network - Get network information'
    ]
  });
});

// Network diagnostics endpoint
app.get('/api/test/network', async (req, res) => {
  try {
    const blockNumber = await web3.eth.getBlockNumber();
    const chainId = await web3.eth.getChainId();
    const gasPrice = await web3.eth.getGasPrice();

    // Verify we're on Ethereum Mainnet
    let networkName = 'Unknown';
    switch (Number(chainId)) {
      case 1:
        networkName = 'Ethereum Mainnet';
        break;
      case 11155111:
        networkName = 'Sepolia Testnet';
        break;
      case 5:
        networkName = 'Goerli Testnet';
        break;
      case 137:
        networkName = 'Polygon Mainnet';
        break;
      default:
        networkName = `Unknown (Chain ID: ${chainId})`;
    }

    // Check contract existence
    const ethContractCode = await web3.eth.getCode(CONTRACTS.CHAINLINK_ETH_USD);
    const btcContractCode = await web3.eth.getCode(CONTRACTS.CHAINLINK_BTC_USD);
    const usdcContractCode = await web3.eth.getCode(CONTRACTS.USDC);

    res.json({
      network: {
        chainId: chainId.toString(),
        name: networkName,
        blockNumber: blockNumber.toString(),
        gasPrice: gasPrice.toString()
      },
      rpcUrl: process.env.WEB3_RPC_URL?.includes('alchemy') ? 'Alchemy' : 'Other',
      contracts: {
        usdc: {
          address: CONTRACTS.USDC,
          hasCode: usdcContractCode.length > 2,
          codeLength: usdcContractCode.length
        },
        ethUsd: {
          address: CONTRACTS.CHAINLINK_ETH_USD,
          hasCode: ethContractCode.length > 2,
          codeLength: ethContractCode.length
        },
        btcUsd: {
          address: CONTRACTS.CHAINLINK_BTC_USD,
          hasCode: btcContractCode.length > 2,
          codeLength: btcContractCode.length
        }
      }
    });
  } catch (error) {
    res.status(500).json({
      error: 'Network verification failed',
      details: error.message
    });
  }
});

// Get ERC-20 token information
app.get('/api/token/:address', async (req, res) => {
  try {
    const { address } = req.params;

    // Validate Ethereum address format
    if (!web3.utils.isAddress(address)) {
      return res.status(400).json({ 
        error: 'Invalid Ethereum address format' 
      });
    }

    // Create contract instance
    const contract = new web3.eth.Contract(ERC20_ABI, address);

    // Fetch token data in parallel for better performance
    const [name, symbol, decimals, totalSupply] = await Promise.all([
      contract.methods.name().call(),
      contract.methods.symbol().call(),
      contract.methods.decimals().call(),
      contract.methods.totalSupply().call()
    ]);

    res.json({
      address,
      name,
      symbol,
      decimals: decimals.toString(),
      totalSupply: totalSupply.toString(),
      // Convert to human-readable format
      totalSupplyFormatted: (Number(totalSupply) / Math.pow(10, Number(decimals))).toLocaleString()
    });
  } catch (error) {
    res.status(500).json({
      error: 'Failed to fetch token information',
      details: error.message
    });
  }
});

// Get ETH/USD price from Chainlink oracle
app.get('/api/chainlink/price/eth', async (req, res) => {
  try {
    console.log('Creating ETH/USD contract with address:', CONTRACTS.CHAINLINK_ETH_USD);

    const contract = new web3.eth.Contract(
      CHAINLINK_PRICE_FEED_ABI,
      CONTRACTS.CHAINLINK_ETH_USD
    );

    console.log('Testing contract methods...');

    // Try the simple latestAnswer method first
    try {
      const latestAnswer = await contract.methods.latestAnswer().call();
      const decimals = await contract.methods.decimals().call();
      const description = await contract.methods.description().call();

      console.log('Simple method successful:', { latestAnswer, decimals, description });

      const price = Number(latestAnswer) / Math.pow(10, Number(decimals));

      res.json({
        pair: 'ETH/USD',
        price: price.toFixed(2),
        rawPrice: latestAnswer.toString(),
        decimals: decimals.toString(),
        description,
        method: 'latestAnswer',
        source: 'Chainlink Decentralized Oracle Network',
        contractAddress: CONTRACTS.CHAINLINK_ETH_USD
      });

    } catch (simpleError) {
      console.log('Simple method failed, trying latestRoundData:', simpleError.message);

      // Fallback to latestRoundData
      const [roundData, decimals, description] = await Promise.all([
        contract.methods.latestRoundData().call(),
        contract.methods.decimals().call(),
        contract.methods.description().call()
      ]);

      console.log('latestRoundData successful:', { roundData, decimals, description });

      const price = Number(roundData.answer) / Math.pow(10, Number(decimals));
      const lastUpdated = new Date(Number(roundData.updatedAt) * 1000);

      res.json({
        pair: 'ETH/USD',
        price: price.toFixed(2),
        rawPrice: roundData.answer.toString(),
        decimals: decimals.toString(),
        description,
        roundId: roundData.roundId.toString(),
        lastUpdated: lastUpdated.toISOString(),
        method: 'latestRoundData',
        source: 'Chainlink Decentralized Oracle Network',
        contractAddress: CONTRACTS.CHAINLINK_ETH_USD
      });
    }

  } catch (error) {
    console.error('ETH/USD Error details:', error);

    res.status(500).json({
      error: 'Failed to fetch ETH/USD price',
      details: error.message,
      contractAddress: CONTRACTS.CHAINLINK_ETH_USD
    });
  }
});

// Get BTC/USD price from Chainlink oracle
app.get('/api/chainlink/price/btc', async (req, res) => {
  try {
    console.log('Creating BTC/USD contract with address:', CONTRACTS.CHAINLINK_BTC_USD);

    const contract = new web3.eth.Contract(
      CHAINLINK_PRICE_FEED_ABI,
      CONTRACTS.CHAINLINK_BTC_USD
    );

    const [roundData, decimals, description] = await Promise.all([
      contract.methods.latestRoundData().call(),
      contract.methods.decimals().call(),
      contract.methods.description().call()
    ]);

    const price = Number(roundData.answer) / Math.pow(10, Number(decimals));
    const lastUpdated = new Date(Number(roundData.updatedAt) * 1000);

    res.json({
      pair: 'BTC/USD',
      price: price.toFixed(2),
      rawPrice: roundData.answer.toString(),
      decimals: decimals.toString(),
      description,
      roundId: roundData.roundId.toString(),
      lastUpdated: lastUpdated.toISOString(),
      source: 'Chainlink Decentralized Oracle Network',
      contractAddress: CONTRACTS.CHAINLINK_BTC_USD
    });
  } catch (error) {
    console.error('BTC/USD Error details:', error);
    res.status(500).json({
      error: 'Failed to fetch BTC/USD price',
      details: error.message,
      contractAddress: CONTRACTS.CHAINLINK_BTC_USD
    });
  }
});

// Start server with connection verification
app.listen(port, () => {
  console.log(`πŸš€ Blockchain API running on port ${port}`);
  console.log(`πŸ“ Endpoints available at http://localhost:${port}/api`);
  console.log(`πŸ”— Web3 RPC: ${process.env.WEB3_RPC_URL || 'NOT SET'}`);

  // Test Web3 connection on startup
  web3.eth.getBlockNumber()
    .then(blockNumber => {
      console.log(`βœ… Connected to Ethereum! Latest block: ${blockNumber}`);
    })
    .catch(error => {
      console.error('❌ Web3 connection failed:', error.message);
    });
});
Enter fullscreen mode Exit fullscreen mode

Package.json

{
  "name": "blockchain-api",
  "version": "1.0.0",
  "description": "Production-ready blockchain API with Chainlink price feeds",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "web3": "^4.0.0",
    "dotenv": "^16.0.3"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  },
  "keywords": ["blockchain", "ethereum", "web3", "api", "chainlink", "defi"],
  "author": "Your Name",
  "license": "MIT"
}
Enter fullscreen mode Exit fullscreen mode

Environment (.env)

WEB3_RPC_URL=https://eth.llamarpc.com
PORT=3000
Enter fullscreen mode Exit fullscreen mode

Gitignore (.gitignore)

node_modules/
.env
.DS_Store
Enter fullscreen mode Exit fullscreen mode

Tags: #blockchain #ethereum #web3 #nodejs #javascript #api #defi #chainlink #cryptocurrency #smartcontracts

Top comments (0)