返回博客

Currency API Node.js Integration Guide

V
Vlado Grigirov
April 05, 2026
Node.js API Currency JavaScript Express Integration

Node.js has become the default runtime for building production backends, APIs, and real-time applications. When you need to handle currency conversion, exchange rates, or multi-currency transactions in Node.js, integrating a currency API correctly is essential for performance, reliability, and cost efficiency. This guide covers everything from basic setup to production-ready patterns including caching strategies, error handling, and retry logic.

Why Node.js + Currency API?

Node.js excels at I/O-bound operations like API requests. A single Node.js instance can handle thousands of concurrent currency conversion requests. Combined with the right currency API (like Finexly), you can build scalable platforms that serve global users in multiple currencies. For browser-based integration, see our JavaScript guide. For Python backends, see the Python tutorial.

Setting Up Your Node.js Project

Start by initializing a new Node.js project and installing dependencies:

mkdir currency-api-app
cd currency-api-app
npm init -y
npm install axios dotenv express

Create a .env file to store your API key securely:

FINEXLY_API_KEY=your_api_key_here
FINEXLY_BASE_URL=https://api.finexly.com/v1

Load environment variables in your main application:

require('dotenv').config();
const axios = require('axios');
const express = require('express');

const app = express();
const API_KEY = process.env.FINEXLY_API_KEY;
const BASE_URL = process.env.FINEXLY_BASE_URL;

Basic API Calls with Async/Await

Here's the simplest approach to fetch currency rates:

async function getExchangeRate(baseCurrency, targetCurrency) {
  try {
    const url = `${BASE_URL}/latest`;
    const response = await axios.get(url, {
      params: {
        base: baseCurrency,
        currencies: targetCurrency,
        apikey: API_KEY
      }
    });

    return response.data.rates[targetCurrency];
  } catch (error) {
    console.error(`Error fetching rate for ${baseCurrency}/${targetCurrency}:`, error.message);
    throw error;
  }
}

// Usage
(async () => {
  const rate = await getExchangeRate('USD', 'EUR');
  console.log(`1 USD = ${rate} EUR`);
})();

This works for simple scripts, but production applications need more sophisticated patterns. Let's build on this foundation.

Building an Express Middleware for Currency Conversion

Create a reusable middleware that adds currency conversion to your Express application:

const currencyMiddleware = async (req, res, next) => {
  // Make conversion functions available throughout the request
  req.convertCurrency = async (amount, from, to) => {
    try {
      const rate = await getExchangeRate(from, to);
      return (amount * rate).toFixed(2);
    } catch (error) {
      throw new Error(`Currency conversion failed: ${error.message}`);
    }
  };

  next();
};

app.use(currencyMiddleware);

// Now use it in routes
app.post('/api/checkout', async (req, res) => {
  const { amount, currency } = req.body;

  try {
    const amountInUSD = await req.convertCurrency(amount, currency, 'USD');
    res.json({
      originalAmount: amount,
      currency: currency,
      amountInUSD: amountInUSD
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Caching Strategies for Performance

Calling the API every time becomes expensive and slow. Implement intelligent caching to reduce API calls and improve response times.

In-Memory Cache with TTL

For simple applications, in-memory cache with time-to-live (TTL) works well:

class CurrencyCache {
  constructor(ttlMinutes = 5) {
    this.cache = {};
    this.ttlMs = ttlMinutes * 60 * 1000;
  }

  generateKey(base, target) {
    return `${base}_${target}`;
  }

  get(base, target) {
    const key = this.generateKey(base, target);
    const cached = this.cache[key];

    if (!cached) return null;
    if (Date.now() > cached.expires) {
      delete this.cache[key];
      return null;
    }

    return cached.rate;
  }

  set(base, target, rate) {
    const key = this.generateKey(base, target);
    this.cache[key] = {
      rate: rate,
      expires: Date.now() + this.ttlMs
    };
  }

  clear() {
    this.cache = {};
  }
}

const cache = new CurrencyCache(5); // 5-minute TTL

async function getExchangeRateCached(base, target) {
  // Check cache first
  const cachedRate = cache.get(base, target);
  if (cachedRate !== null) {
    console.log(`Cache hit: ${base}/${target}`);
    return cachedRate;
  }

  // Fetch from API
  const rate = await getExchangeRate(base, target);
  cache.set(base, target, rate);
  return rate;
}

Error Handling and Retry Logic

Production applications must handle API failures gracefully:

async function getExchangeRateWithRetry(base, target, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const rate = await getExchangeRateCached(base, target);
      return rate;
    } catch (error) {
      if (attempt === maxRetries) {
        throw new Error(`Failed to fetch ${base}/${target} after ${maxRetries} attempts`);
      }

      // Exponential backoff: wait 1s, 2s, 4s between retries
      const delayMs = Math.pow(2, attempt - 1) * 1000;
      console.warn(`Attempt ${attempt} failed, retrying in ${delayMs}ms...`);
      await new Promise(resolve => setTimeout(resolve, delayMs));
    }
  }
}

app.get('/api/rate', async (req, res) => {
  const { base, target } = req.query;

  try {
    const rate = await getExchangeRateWithRetry(base, target);
    res.json({ base, target, rate });
  } catch (error) {
    res.status(503).json({
      error: 'Exchange rate service temporarily unavailable',
      details: error.message
    });
  }
});

Production-Ready Currency Conversion Service Class

Here's a complete, production-grade service encapsulating all the patterns above:

class CurrencyService {
  constructor(apiKey, baseUrl, cacheTtlMinutes = 5) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
    this.cache = new CurrencyCache(cacheTtlMinutes);
    this.rateLimiter = { lastCall: 0, minInterval: 100 }; // 100ms between API calls
  }

  async _rateLimitedFetch(url, params) {
    // Prevent API rate limiting by spacing out calls
    const now = Date.now();
    const timeSinceLastCall = now - this.rateLimiter.lastCall;

    if (timeSinceLastCall < this.rateLimiter.minInterval) {
      await new Promise(resolve =>
        setTimeout(resolve, this.rateLimiter.minInterval - timeSinceLastCall)
      );
    }

    this.rateLimiter.lastCall = Date.now();
    return axios.get(url, { params });
  }

  async getRate(base, target, maxRetries = 3) {
    // Try cache first
    const cached = this.cache.get(base, target);
    if (cached !== null) return cached;

    // Fetch with retries
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const url = `${this.baseUrl}/latest`;
        const response = await this._rateLimitedFetch(url, {
          base: base,
          currencies: target,
          apikey: this.apiKey
        });

        const rate = response.data.rates[target];
        this.cache.set(base, target, rate);
        return rate;
      } catch (error) {
        if (attempt === maxRetries) throw error;
        const delayMs = Math.pow(2, attempt - 1) * 1000;
        await new Promise(resolve => setTimeout(resolve, delayMs));
      }
    }
  }

  async convertAmount(amount, fromCurrency, toCurrency) {
    const rate = await this.getRate(fromCurrency, toCurrency);
    return parseFloat((amount * rate).toFixed(2));
  }

  clearCache() {
    this.cache.clear();
  }
}

// Initialize service
const currencyService = new CurrencyService(
  process.env.FINEXLY_API_KEY,
  process.env.FINEXLY_BASE_URL,
  5 // 5-minute cache
);

// Use in Express app
app.post('/api/convert', async (req, res) => {
  const { amount, from, to } = req.body;

  try {
    const converted = await currencyService.convertAmount(amount, from, to);
    res.json({
      original: { amount, currency: from },
      converted: { amount: converted, currency: to }
    });
  } catch (error) {
    res.status(500).json({ error: 'Conversion failed' });
  }
});

Performance Tips for Node.js Currency Applications

Batch Multiple Pairs: Instead of fetching rates one pair at a time, request multiple pairs in a single API call. Finexly supports this via the pairs parameter.

Use Connection Pooling: Node.js maintains HTTP connections efficiently, but for high-volume applications, configure axios to reuse connections:

const agent = new axios.Agent({ keepAlive: true });
axios.defaults.httpAgent = agent;

Monitor Cache Hit Rates: Track how often your cache succeeds to optimize TTL:

class CurrencyCache {
  constructor(ttlMinutes = 5) {
    // ... existing code ...
    this.stats = { hits: 0, misses: 0 };
  }

  get(base, target) {
    const key = this.generateKey(base, target);
    const cached = this.cache[key];

    if (cached && Date.now() <= cached.expires) {
      this.stats.hits++;
      return cached.rate;
    }

    this.stats.misses++;
    return null;
  }

  getHitRate() {
    const total = this.stats.hits + this.stats.misses;
    return total === 0 ? 0 : ((this.stats.hits / total) * 100).toFixed(1);
  }
}

Next Steps

This guide provides production-ready patterns that scale from small prototypes to high-volume applications handling thousands of currency conversion requests per minute. The combination of intelligent caching, rate limiting, retry logic, and error handling ensures reliability while keeping API costs low.

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →