Developer Guide

Building in Web3: The Essential Role of RPC Nodes

Every Web3 application, whether a simple wallet or a complex DeFi protocol, depends on a critical piece of infrastructure: RPC nodes. Understanding what RPC nodes do, why they matter, and how to integrate them effectively is essential knowledge for any blockchain developer.

What Are RPC Nodes?

RPC (Remote Procedure Call) nodes are servers that provide access to blockchain data and functionality. They act as the bridge between your application and the blockchain network, handling the complex work of:

  • Querying blockchain state
  • Submitting transactions
  • Streaming real-time updates
  • Validating data integrity
  • The simple analogy: If the blockchain is a database, RPC nodes are the database servers you connect to. Your application never talks to "the blockchain" directly—it talks to nodes that participate in the network.

    Your App  <-->  RPC Node  <-->  Blockchain Network
       |              |                    |
       |   HTTP/WS    |     P2P Protocol   |
       |   Requests   |     Consensus      |

    Why RPC Nodes Matter for Web3 Development

    1. They Determine Your App's Reliability

    If your RPC node goes down, your application stops working. Users can't check balances, submit transactions, or interact with smart contracts. Node reliability directly translates to application uptime.

    2. They Impact User Experience

    RPC response times affect how snappy your application feels. A slow node means:

  • Delayed balance updates
  • Sluggish transaction confirmations
  • Poor real-time data streaming
  • Frustrated users
  • 3. They Affect Your Costs

    Different node providers have vastly different pricing models. Understanding your usage patterns helps optimize costs:

    Usage PatternBest Approach
    Development/TestingFree tiers, local nodes
    Low-traffic dAppsPay-per-request
    High-volume applicationsDedicated nodes, bulk pricing
    Mission-critical systemsMultiple providers, failover

    RPC Node Architecture

    Understanding how nodes work helps you build better applications.

    Full Nodes vs. Archive Nodes

    Full Nodes:

  • Store recent blockchain state
  • Can validate new transactions
  • Sufficient for most applications
  • Lower storage requirements
  • Archive Nodes:

  • Store complete historical state
  • Can query any point in history
  • Required for historical analytics
  • Significantly higher storage costs
  • The Request Lifecycle

    When your application makes an RPC request:

    1. App sends HTTP/WebSocket request to node
    2. Node validates the request format
    3. Node queries local blockchain data
    4. Node formats response according to API spec
    5. Response returned to application

    For write operations (transactions):

    1. App submits signed transaction to node
    2. Node validates transaction format and signatures
    3. Node broadcasts to network peers
    4. Network reaches consensus
    5. Transaction included in block
    6. Node confirms to application

    Stellar's Horizon: A Purpose-Built RPC Layer

    Stellar takes a different approach than most blockchains. Instead of raw node access, Stellar provides Horizon—a RESTful API server that wraps Stellar Core functionality.

    Horizon advantages:

  • Clean RESTful API design
  • Built-in pagination and filtering
  • Real-time streaming via Server-Sent Events
  • Historical data readily accessible
  • No need to parse binary protocols
  • Example Horizon requests through LumenQuery:

    # Get account information
    curl -H "X-API-Key: your_key" \
      "https://api.lumenquery.io/accounts/GA..."
    
    # Get recent transactions
    curl -H "X-API-Key: your_key" \
      "https://api.lumenquery.io/transactions?limit=10&order=desc"
    
    # Stream payments in real-time
    curl -H "X-API-Key: your_key" \
      "https://api.lumenquery.io/payments?cursor=now"

    Best Practices for RPC Integration

    1. Never Hardcode Node URLs

    Always use environment variables or configuration:

    // Bad
    const server = new Horizon.Server('https://api.lumenquery.io');
    
    // Good
    const server = new Horizon.Server(process.env.HORIZON_URL, {
      headers: { 'X-API-Key': process.env.API_KEY }
    });

    2. Implement Retry Logic

    Network requests fail. Plan for it:

    async function fetchWithRetry(url, options, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          const response = await fetch(url, options);
          if (response.ok) return response.json();
    
          // Don't retry client errors
          if (response.status >= 400 && response.status < 500) {
            throw new Error(`Client error: ${response.status}`);
          }
        } catch (error) {
          if (i === maxRetries - 1) throw error;
    
          // Exponential backoff
          await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
        }
      }
    }

    3. Use Connection Pooling

    For high-throughput applications, reuse connections:

    import { Agent } from 'https';
    
    const agent = new Agent({
      keepAlive: true,
      maxSockets: 50
    });
    
    // Reuse agent across requests
    const response = await fetch(url, { agent });

    4. Cache Appropriately

    Not all data needs fresh fetches:

    const cache = new Map();
    const CACHE_TTL = 5000; // 5 seconds
    
    async function getCachedAccount(accountId) {
      const cached = cache.get(accountId);
      if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
        return cached.data;
      }
    
      const data = await fetchAccount(accountId);
      cache.set(accountId, { data, timestamp: Date.now() });
      return data;
    }

    5. Handle Rate Limits Gracefully

    Respect provider rate limits:

    async function handleRateLimit(response) {
      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After') || 60;
        console.log(`Rate limited. Waiting ${retryAfter} seconds.`);
        await new Promise(r => setTimeout(r, retryAfter * 1000));
        return true; // Signal to retry
      }
      return false;
    }

    Running Your Own Nodes vs. Using Providers

    Self-Hosted Nodes

    Pros:

  • Complete control over infrastructure
  • No external dependencies
  • No rate limits
  • Potentially lower long-term costs at scale
  • Cons:

  • Significant operational overhead
  • Requires blockchain expertise
  • Hardware and bandwidth costs
  • Responsibility for uptime and updates
  • Managed Providers (like LumenQuery)

    Pros:

  • Zero infrastructure management
  • Guaranteed uptime SLAs
  • Automatic updates and scaling
  • Pay only for what you use
  • Expert support available
  • Cons:

  • External dependency
  • Rate limits on lower tiers
  • Less control over node configuration
  • Hybrid Approach

    Many production applications use both:

    const providers = [
      { url: process.env.PRIMARY_NODE, priority: 1 },
      { url: process.env.BACKUP_NODE, priority: 2 },
      { url: process.env.SELF_HOSTED_NODE, priority: 3 }
    ];
    
    async function resilientRequest(path) {
      for (const provider of providers) {
        try {
          return await fetch(provider.url + path);
        } catch (error) {
          console.log(`Provider ${provider.url} failed, trying next`);
        }
      }
      throw new Error('All providers failed');
    }

    Monitoring RPC Health

    Key Metrics to Track

    Latency:

    const start = Date.now();
    await fetch(nodeUrl + '/health');
    const latency = Date.now() - start;
    console.log(`Node latency: ${latency}ms`);

    Success Rate:

    let requests = 0;
    let failures = 0;
    
    // Track over time
    const successRate = ((requests - failures) / requests) * 100;

    Sync Status:

    # Check if node is synced with network
    curl "https://api.lumenquery.io/ledgers?limit=1&order=desc"
    # Compare latest ledger with known network state

    LumenQuery: Stellar RPC Made Simple

    LumenQuery handles the complexity of running Stellar infrastructure so you can focus on building:

    What we manage:

  • Stellar Core and Horizon servers
  • Database optimization and scaling
  • Network synchronization
  • Security and updates
  • 99.9% uptime guarantee
  • What you get:

  • Simple REST API access
  • Real-time streaming
  • Historical data queries
  • Usage analytics
  • Dedicated support
  • Quick start:

    import { Horizon } from '@stellar/stellar-sdk';
    
    const server = new Horizon.Server('https://api.lumenquery.io', {
      headers: { 'X-API-Key': 'lq_your_key' }
    });
    
    // You're ready to build
    const account = await server.loadAccount('GA...');
    console.log('Balances:', account.balances);

    Common Pitfalls to Avoid

    1. Ignoring Network Differences

    Testnet and mainnet behave differently. Always verify your node is pointing to the correct network:

    const response = await fetch(nodeUrl + '/');
    const { network_passphrase } = await response.json();
    
    if (network_passphrase !== expectedNetwork) {
      throw new Error('Connected to wrong network!');
    }

    2. Not Handling Pagination

    Large result sets require pagination:

    async function getAllTransactions(accountId) {
      let transactions = [];
      let cursor = '';
    
      while (true) {
        const url = `/accounts/${accountId}/transactions?limit=200&cursor=${cursor}`;
        const response = await fetch(nodeUrl + url);
        const data = await response.json();
    
        transactions = transactions.concat(data._embedded.records);
    
        if (data._embedded.records.length < 200) break;
        cursor = data._embedded.records.slice(-1)[0].paging_token;
      }
    
      return transactions;
    }

    3. Trusting Unvalidated Data

    Always validate critical data from external sources:

    // Verify transaction success before updating state
    if (transaction.successful !== true) {
      throw new Error('Transaction failed');
    }
    
    // Verify amounts match expectations
    if (payment.amount !== expectedAmount) {
      throw new Error('Amount mismatch');
    }

    Conclusion

    RPC nodes are the foundation of every Web3 application. Understanding how they work, implementing robust integration patterns, and choosing the right infrastructure approach are essential skills for blockchain developers.

    Whether you're building a simple wallet, a complex DeFi protocol, or anything in between, reliable RPC infrastructure determines your application's success. With managed services like LumenQuery, you can skip the operational complexity and focus on what matters: building great applications.


    *Ready to build on Stellar with reliable RPC infrastructure? Sign up for LumenQuery—free tier includes 10,000 requests per month.*