A simple and efficient money and currency conversion and formatting tool for JavaScript and TypeScript projects. Format currency with ease, convert between currencies using live exchange rates, and chain operations elegantly.
- Easy Currency Formatting - Format numbers as currency with proper symbols and formatting
- Live Exchange Rates - Convert between currencies using real-time exchange rates
- Exchange Rate Providers - Choose between ExchangeRate-API, Frankfurter, or another compatible provider
- Mathematical Operations - Perform calculations with proper precision (add, subtract, multiply, divide, etc.)
- Chainable API - Fluent, intuitive method chaining for complex operations
- TypeScript Support - Full type safety with TypeScript definitions
- Multiple Currencies - Support for all major world currencies
- Zero Runtime Dependencies - No external dependencies, lightweight and fast
npm install @toneflix/money
pnpm add @toneflix/money
yarn add @toneflix/money
import { Money } from '@toneflix/money';
// Format a number as currency
Money.format(1234.56, 'USD'); // "$1,234.56"
Money.format(1234.56, 'EUR'); // "€1,234.56"
Money.format(1234.56, 'GBP'); // "£1,234.56"
// Using instance methods
const money = new Money(1234.56, 'USD');
money.format(); // "$1,234.56"
money.whole(); // "$1,234" (no decimals)
money.compact(); // "$1.2K" (compact notation)By default, currency conversion uses ExchangeRateApi, preserving the existing Exchange.setApiKey() and EXCHANGERATE_API_KEY configuration.
import { Exchange } from '@toneflix/money';
// ExchangeRateApi is the default provider.
// Set your API key in code...
Exchange.setApiKey('your-api-key-here');
// ...or use EXCHANGERATE_API_KEY in your environment.
// Convert currency with the chainable API
const result = await Exchange.from('USD').to('EUR').convert(100);
console.log(result); // e.g., 92.5 (euros)
// Get formatted result
const formatted = await Exchange.from('USD').to('GBP').convert(100).format();
console.log(formatted); // e.g., "£85.23"To use Frankfurter instead, select FrankfurterApi. Frankfurter does not require an API key.
import { Exchange, FrankfurterApi } from '@toneflix/money';
Exchange.setProvider(FrankfurterApi);
const result = await Exchange.from('USD').to('EUR').convert(100);The Money class handles currency formatting and display.
new Money(amount?: number | string, currency?: CurrencyCode)Parameters:
amount- The monetary amount to formatcurrency- Optional currency code (e.g., 'USD', 'EUR', 'GBP')
Example:
const money = new Money(1234.56, 'USD');Set the default currency for all Money instances.
Money.setDefaultCurrency('EUR');
const money = new Money(100); // Uses EUR by defaultFormat an amount as currency (static method).
Money.format(1234.56, 'USD'); // "$1,234.56"
Money.format(1234.56, 'EUR'); // "€1,234.56"
Money.format(1234.56, 'JPY'); // "¥1,234.56"Format an amount without decimal places (static method).
Money.whole(1234.56, 'USD'); // "$1,234"Format an amount in compact notation (static method).
Money.compact(1234567, 'USD'); // "$1.2M"
Money.compact(1234, 'USD'); // "$1.2K"
Money.compact(1234567890, 'USD'); // "$1.2B"Convert and format amount between currencies (static method).
const converted = await Money.convert(100, 'USD', 'EUR');
console.log(converted.format()); // e.g., "€92.50"Get the default currency code.
Money.currencyCode(); // "USD"Get the default currency symbol.
Money.currencySymbol(); // "$"Create a new Money instance (factory method).
const money = Money.of(100, 'USD');Format the amount with currency symbol and proper formatting.
const money = new Money(1234.56, 'USD');
money.format(); // "$1,234.56"Format the amount without decimal places.
const money = new Money(1234.56, 'USD');
money.whole(); // "$1,234"Format the amount in compact notation (K, M, B, T).
const money = new Money(1234567, 'USD');
money.compact(); // "$1.2M"Convert the amount to another currency.
const money = new Money(100, 'USD');
const converted = await money.convert('EUR');
console.log(converted.format()); // e.g., "€92.50"Set how negative amounts should be displayed.
const money = new Money(-100, 'USD');
money.setNegativeStyle('minus');
money.format(); // "-$100.00"
money.setNegativeStyle('parentheses');
money.format(); // "($100.00)"Get the current currency code.
const money = new Money(100, 'EUR');
money.currencyCode(); // "EUR"Get the current currency symbol.
const money = new Money(100, 'GBP');
money.currencySymbol(); // "£"Get string representation (same as format()).
const money = new Money(100, 'USD');
money.toString() // "$100.00"
`${money}`; // "$100.00"Add another amount to this Money instance.
const money = new Money(100, 'USD');
const result = money.add(50);
console.log(result.format()); // "$150.00"
// Can also add another Money instance
const other = new Money(25, 'USD');
money.add(other).format(); // "$125.00"Subtract another amount from this Money instance.
const money = new Money(100, 'USD');
const result = money.subtract(30);
console.log(result.format()); // "$70.00"Multiply this Money instance by a factor.
const money = new Money(50, 'USD');
const result = money.multiply(3);
console.log(result.format()); // "$150.00"Divide this Money instance by a divisor.
const money = new Money(100, 'USD');
const result = money.divide(4);
console.log(result.format()); // "$25.00"Round this Money instance up to the nearest integer.
const money = new Money(99.45, 'USD');
const result = money.ceil();
console.log(result.format()); // "$100.00"Round this Money instance down to the nearest integer.
const money = new Money(99.95, 'USD');
const result = money.floor();
console.log(result.format()); // "$99.00"Round this Money instance to specified decimal digits.
const money = new Money(99.456, 'USD');
money.round().format(); // "$99.00" (default: 0 decimals)
money.round(1).format(); // "$99.50"
money.round(2).format(); // "$99.46"Calculate modulus (remainder) of this Money instance by divisor.
const money = new Money(100, 'USD');
const result = money.mod(30);
console.log(result.format()); // "$10.00" (100 % 30 = 10)Get absolute value of this Money instance.
const money = new Money(-100, 'USD');
const result = money.absolute();
console.log(result.format()); // "$100.00"Calculate share of this Money instance based on total and ratio.
const money = new Money(100, 'USD');
const result = money.share(1000, 0.1); // 10% share
console.log(result.format()); // "$10.00"
// Calculate proportional share
const part = new Money(25, 'USD');
const total = 100;
const ratio = 0.5; // 50% ratio
part.share(total, ratio).format(); // "$12.50"The Exchange class handles currency conversion with live exchange rates through a configurable exchange-rate provider.
new Exchange(source?: CurrencyCode, target?: CurrencyCode, amount?: number)Parameters:
source- Source currency code (optional)target- Target currency code (optional)amount- Amount to convert (optional, default: 1)
Example:
const exchange = new Exchange('USD', 'EUR', 100);Set the API key used by providers that support API-key authentication.
This method is preserved for backwards compatibility. With the default ExchangeRateApi provider, the key can be configured either with Exchange.setApiKey() or the EXCHANGERATE_API_KEY environment variable.
Exchange.setApiKey('your-api-key-here');FrankfurterApi does not require an API key, so Exchange.setApiKey() is not required when Frankfurter is selected.
Set the exchange-rate provider used by Exchange.
Pass the provider class itself, not an instance.
import { Exchange, ExchangeRateApi, FrankfurterApi } from '@toneflix/money';
// ExchangeRateApi preserves the existing API-key based behaviour.
Exchange.setProvider(ExchangeRateApi);
Exchange.setApiKey('your-api-key-here');
// Or switch to Frankfurter, which does not require an API key.
Exchange.setProvider(FrankfurterApi);The configured provider is used by subsequent Exchange conversions and rate requests.
Create a new Exchange instance with source currency (static method).
const exchange = Exchange.from('USD');Create a new Exchange instance with target currency (static method).
const exchange = Exchange.to('EUR');Convert and format amount in one call (static method).
const formatted = await Exchange.format(100, 'USD', 'EUR');
console.log(formatted); // e.g., "€92.50"Set the source currency.
const exchange = new Exchange();
exchange.from('USD');Set the target currency.
const exchange = new Exchange();
exchange.to('EUR');Convert an amount between currencies. Returns this for chaining.
// Basic usage
const result = await exchange.convert(100);
// With optional currencies
const result = await exchange.convert(100, 'USD', 'EUR');
// Chainable
const result = await exchange.from('USD').to('EUR').convert(100);Get the exchange rate between two currencies. Returns this for chaining.
// Get exchange rate
const rate = await exchange.rate('USD', 'EUR');
console.log(rate); // e.g., 0.925
// Chainable
const rate = await exchange.from('USD').to('EUR').rate();Format the converted amount with currency symbol.
const formatted = await exchange.from('USD').to('EUR').convert(100).format();
console.log(formatted); // e.g., "€92.50"The Exchange class implements a "thenable" interface, making it work seamlessly with async/await and Promises:
// Methods return 'this' for chaining (synchronous)
const chain = exchange.from('USD').to('EUR').convert(100);
// Execution happens when awaited or .then() is called
const result = await chain;
// Or with .then()
chain.then((result) => {
console.log(result); // Converted amount
});
// With error handling
chain
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log('Done'));How it works:
- All methods (
from(),to(),convert(),rate()) returnthissynchronously - The chain stays synchronous until you await it or call
.then() - When awaited/then'd, it automatically executes the API call
- This gives you full control over when the async operation happens
Exchange delegates live currency conversion to a configured provider. This keeps the chainable Exchange API the same while allowing different exchange-rate services to be used.
ExchangeRateApi is the backwards-compatible/default provider and uses ExchangeRate-API.
It requires an API key. Existing configuration continues to work:
import { Exchange, ExchangeRateApi } from '@toneflix/money';
Exchange.setProvider(ExchangeRateApi);
// Runtime configuration
Exchange.setApiKey('your-api-key-here');
const result = await Exchange.from('USD').to('EUR').convert(100);You can also configure the provider through the environment:
EXCHANGERATE_API_KEY=your-api-key-here
When both are available, the API key explicitly supplied through Exchange.setApiKey() is used before the provider's environment fallback.
FrankfurterApi provides currency rates without requiring an API key.
import { Exchange, FrankfurterApi } from '@toneflix/money';
Exchange.setProvider(FrankfurterApi);
const amount = await Exchange.from('USD').to('EUR').convert(100);
const rate = await Exchange.from('USD').to('EUR').rate();Frankfurter returns an exchange rate, and the provider derives the converted amount from that rate so the public Exchange API behaves consistently across providers.
Provider selection is global to Exchange. Set the provider before creating or executing exchange operations:
import { Exchange, ExchangeRateApi, FrankfurterApi } from '@toneflix/money';
// API-key based provider
Exchange.setProvider(ExchangeRateApi);
Exchange.setApiKey('your-api-key-here');
const apiResult = await Exchange.from('USD').to('EUR').convert(100);
// Keyless provider
Exchange.setProvider(FrankfurterApi);
const frankfurterResult = await Exchange.from('USD').to('EUR').convert(100);The rest of the API remains unchanged: from(), to(), convert(), rate(), format(), and the thenable/awaitable chain work with the selected provider.
import { Money } from '@toneflix/money';
// Different currencies
console.log(Money.format(1234.56, 'USD')); // "$1,234.56"
console.log(Money.format(1234.56, 'EUR')); // "€1,234.56"
console.log(Money.format(1234.56, 'GBP')); // "£1,234.56"
console.log(Money.format(1234.56, 'JPY')); // "¥1,234.56"
// Different formats
console.log(Money.format(1234.56, 'USD')); // "$1,234.56"
console.log(Money.whole(1234.56, 'USD')); // "$1,234"
console.log(Money.compact(1234567, 'USD')); // "$1.2M"import { Money } from '@toneflix/money';
const money = new Money(-1234.56, 'USD');
// Minus sign (default)
money.setNegativeStyle('minus');
console.log(money.format()); // "-$1,234.56"
// Parentheses (accounting style)
money.setNegativeStyle('parentheses');
console.log(money.format()); // "($1,234.56)"import { Money } from '@toneflix/money';
console.log(Money.compact(999, 'USD')); // "$999"
console.log(Money.compact(1234, 'USD')); // "$1.2K"
console.log(Money.compact(1234567, 'USD')); // "$1.2M"
console.log(Money.compact(1234567890, 'USD')); // "$1.2B"
console.log(Money.compact(1234567890123, 'USD')); // "$1.2T"import { Money } from '@toneflix/money';
const price = new Money(99.99, 'USD');
// Addition
const withTax = price.add(7.5);
console.log(withTax.format()); // "$107.49"
// Subtraction
const withDiscount = price.subtract(10);
console.log(withDiscount.format()); // "$89.99"
// Multiplication
const quantity = price.multiply(3);
console.log(quantity.format()); // "$299.97"
// Division
const perItem = price.divide(2);
console.log(perItem.format()); // "$49.995" or rounded
// Rounding
const rounded = price.round(2);
console.log(rounded.format()); // "$100.00"
// Chaining operations
const total = new Money(100, 'USD').add(50).multiply(2).subtract(25).round();
console.log(total.format()); // "$275.00"
// Calculate percentage/share
const amount = new Money(100, 'USD');
const tip = amount.share(100, 0.15); // 15% tip
console.log(tip.format()); // "$15.00"
// Absolute value
const debt = new Money(-50, 'USD');
console.log(debt.absolute().format()); // "$50.00"
// Modulus
const change = new Money(100, 'USD').mod(30);
console.log(change.format()); // "$10.00"Using the default ExchangeRateApi provider:
import { Exchange } from '@toneflix/money';
Exchange.setApiKey('your-api-key-here');
// Simple conversion
const amount = await Exchange.from('USD').to('EUR').convert(100);
console.log(amount); // e.g., 92.5
// Get exchange rate
const rate = await Exchange.from('USD').to('EUR').rate();
console.log(rate); // e.g., 0.925
// Convert and format
const formatted = await Exchange.from('USD').to('GBP').convert(100).format();
console.log(formatted); // e.g., "£85.23"Using FrankfurterApi:
import { Exchange, FrankfurterApi } from '@toneflix/money';
Exchange.setProvider(FrankfurterApi);
const amount = await Exchange.from('USD').to('EUR').convert(100);
const rate = await Exchange.from('USD').to('EUR').rate();import { Money } from '@toneflix/money';
Money.setDefaultCurrency('USD');
const money = new Money(100);
console.log(money.format()); // "$100.00"
// Convert to another currency
const converted = await money.convert('EUR');
console.log(converted.format()); // e.g., "€92.50"
// Static conversion
const result = await Money.convert(100, 'USD', 'GBP');
console.log(result.format()); // e.g., "£85.23"import { Exchange } from '@toneflix/money';
Exchange.setApiKey('your-api-key-here');
// Build the chain (synchronous)
const chain = Exchange.from('USD').to('EUR').convert(1000);
// Execute when ready (asynchronous)
const result = await chain;
console.log(result); // e.g., 925.0
// Or with callbacks
chain
.then((result) => {
console.log(`Converted: ${result}`);
return result;
})
.then((result) => {
// Chain more operations
return result * 1.1;
})
.catch((error) => {
console.error('Conversion failed:', error);
})
.finally(() => {
console.log('Conversion complete');
});This library has zero runtime dependencies - you don't need dotenv or any other package!
Environment-based API-key configuration applies to ExchangeRateApi. FrankfurterApi does not require an API key.
Set the ExchangeRate-API key in your preferred way:
Option 1: Using your own dotenv (if you have one)
# .env file
EXCHANGERATE_API_KEY=your-api-key-here
Option 2: System environment variables
# Terminal
export EXCHANGERATE_API_KEY=your-api-key-here
# Or in your deployment platform (Vercel, Netlify, etc.)
Option 3: Runtime configuration
import { Exchange, ExchangeRateApi } from '@toneflix/money';
Exchange.setProvider(ExchangeRateApi);
Exchange.setApiKey('your-api-key-here');Then use it:
import { Exchange } from '@toneflix/money';
// ExchangeRateApi can resolve the key from Exchange.setApiKey()
// or its supported environment configuration.
const result = await Exchange.from('USD').to('EUR').convert(100);If you select FrankfurterApi, no key configuration is necessary:
import { Exchange, FrankfurterApi } from '@toneflix/money';
Exchange.setProvider(FrankfurterApi);
const result = await Exchange.from('USD').to('EUR').convert(100);import { Exchange } from '@toneflix/money';
try {
const result = await Exchange.from('USD').to('EUR').convert(100);
console.log(result);
} catch (error) {
if (error.type === 'missing-key') {
console.error('Please configure an API key for the selected provider');
} else {
console.error('Conversion error:', error.message);
}
}The library supports all major world currencies including but not limited to:
- 🇺🇸 USD (US Dollar)
- 🇪🇺 EUR (Euro)
- 🇬🇧 GBP (British Pound)
- 🇯🇵 JPY (Japanese Yen)
- 🇨🇦 CAD (Canadian Dollar)
- 🇦🇺 AUD (Australian Dollar)
- 🇨🇭 CHF (Swiss Franc)
- 🇨🇳 CNY (Chinese Yuan)
- 🇮🇳 INR (Indian Rupee)
- And many more...
See the full list in the currencies.ts file.
Authentication depends on the selected exchange-rate provider.
ExchangeRateApi requires an API key from ExchangeRate-API.
Getting Started:
- Visit exchangerate-api.com
- Sign up for an account
- Copy your API key
- Configure it using
Exchange.setApiKey()or the supported environment variable:
import { Exchange, ExchangeRateApi } from '@toneflix/money';
Exchange.setProvider(ExchangeRateApi);
// Method 1: Set in code
Exchange.setApiKey('your-api-key-here');
// Method 2: Use environment configuration
// EXCHANGERATE_API_KEY=your-api-key-hereExisting code that only calls Exchange.setApiKey() remains compatible because ExchangeRateApi is the backwards-compatible provider.
FrankfurterApi does not require an API key.
import { Exchange, FrankfurterApi } from '@toneflix/money';
Exchange.setProvider(FrankfurterApi);
const result = await Exchange.from('USD').to('EUR').convert(100);No Exchange.setApiKey() call or EXCHANGERATE_API_KEY environment variable is needed when using Frankfurter.
This library is written in TypeScript and provides full type definitions out of the box.
import { Money, Exchange, CurrencyCode, FrankfurterApi } from '@toneflix/money';
// CurrencyCode type ensures type safety
const currency: CurrencyCode = 'USD';
// Full IDE autocomplete and type checking
const money = new Money(100, currency);
const formatted: string = money.format();
// Async operations are properly typed
const result: number = await Exchange.from('USD').to('EUR').convert(100);
// Providers are selected by class
Exchange.setProvider(FrankfurterApi);Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the ISC License - see the LICENSE file for details.
- Issues: GitHub Issues
- Documentation: GitHub Repository
- Discussions: GitHub Discussions
Created by Toneflix Technologies Limited
Exchange rates can be powered by ExchangeRate-API or Frankfurter, depending on the configured provider.