
Dinero Currency Patterns
- 11 installs
- 6.8k repo stars
- Updated August 1, 2026
- dinerojs/dinero.js
Handle multi-currency conversion, custom currencies, database storage, and payment integration with Dinero.js.
About
Currency-handling patterns for Dinero.js covering conversion, custom currencies, storage, and payment integration. A developer uses it when working with multiple currencies or storing money in a database.
- Type-safe custom currencies with as-const satisfies and compile-time mismatch checks
- Conversion with scaled rates, database storage patterns, and Stripe/PayPal payment integration
Dinero Currency Patterns by the numbers
- 11 all-time installs (skills.sh)
- Ranked #798 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dinerojs/dinero.js --skill dinero-currency-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11 |
|---|---|
| repo stars | ★ 6.8k |
| Last updated | August 1, 2026 |
| Repository | dinerojs/dinero.js ↗ |
What it does
Handle multi-currency conversion, custom currencies, database storage, and payment integration with Dinero.js.
Files
Dinero.js Currency Patterns
Patterns for handling currencies with Dinero.js: type safety, conversions, custom currencies, database storage, and payment service integration.
When to Apply
Reference these guidelines when:
- Converting between currencies with
convert - Defining custom currencies (e.g., cryptocurrencies, loyalty points)
- Looking up currencies dynamically from external input
- Storing monetary values in a database
- Integrating with payment services (Stripe, PayPal, Square)
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Type Safety | HIGH | types- |
| 2 | Conversion | HIGH | convert- |
| 3 | Storage | HIGH | storage- |
| 4 | Payment Integration | MEDIUM | payment- |
Quick Reference
1. Type Safety (HIGH)
types-as-const- Define custom currencies withas const satisfiesfor compile-time safetytypes-currency-mismatch- TypeScript catches currency mismatches in operations at compile timetypes-lookup-validation- Validate currency codes from external sources at runtime
2. Conversion (HIGH)
convert-scaled-rates- Use scaled amounts for fractional exchange rates, not floatsconvert-reusable- Build reusable converter functions with higher-order patterns
3. Storage (HIGH)
storage-database- Store amount, currency code, and scale as separate columnsstorage-no-money-type- Avoid PostgreSQL'smoneytype for multi-currency applications
4. Payment Integration (MEDIUM)
payment-services- Map Dinero objects to payment service formats with dedicated helpers
How to Use
Read individual rule files for detailed explanations and code examples:
rules/types-as-const.md
rules/convert-scaled-rates.mdEach rule file contains:
- Brief explanation of why it matters
- Incorrect code example with explanation
- Correct code example with explanation
- Additional context and references
Build Reusable Converter Functions
When converting many objects with the same rates, wrap convert in a higher-order function to avoid passing rates every time.
Correct (reusable converter):
import { dinero, convert } from 'dinero.js';
import { USD, EUR } from 'dinero.js/currencies';
function createConverter(rates) {
return function converter(dineroObject, newCurrency) {
return convert(dineroObject, newCurrency, rates);
};
}
const rates = { EUR: { amount: 89, scale: 2 } };
const convertWithRates = createConverter(rates);
const price = dinero({ amount: 500, currency: USD });
convertWithRates(price, EUR); // Dinero object in EURThis pattern works well when rates are fetched once per request or session and reused across multiple conversions.
Reference: https://v2.dinerojs.com/api/conversions/convert
Use Scaled Amounts for Fractional Exchange Rates, Not Floats
When converting between currencies, fractional exchange rates should be passed as scaled amounts ({ amount, scale }) instead of floats. This avoids floating-point precision issues.
Incorrect (float rate):
import { dinero, convert } from 'dinero.js';
import { USD, EUR } from 'dinero.js/currencies';
const rates = { EUR: 0.89 }; // Float — imprecise
const d = dinero({ amount: 500, currency: USD });
convert(d, EUR, rates);Correct (scaled rate):
import { dinero, convert } from 'dinero.js';
import { USD, EUR } from 'dinero.js/currencies';
const rates = { EUR: { amount: 89, scale: 2 } }; // 89/100 = 0.89 — precise
const d = dinero({ amount: 500, currency: USD });
convert(d, EUR, rates); // Dinero object with amount 44500, scale 4Integer rates can be passed directly without scaling:
const rates = { IQD: 1199 }; // 1 USD = 1199 IQD (integer, no scaling needed)
convert(d, IQD, rates);Reference: https://v2.dinerojs.com/api/conversions/convert
Map Dinero Objects to Payment Service Formats with Dedicated Helpers
Each payment service expects a different money format. Build dedicated helper functions to convert Dinero objects to the right shape.
Stripe (minor units, lowercase currency):
import { toSnapshot } from 'dinero.js';
function toStripeMoney(dineroObject) {
const { amount, currency } = toSnapshot(dineroObject);
return {
amount,
currency: currency.code.toLowerCase(),
};
}
// { amount: 1999, currency: 'usd' }PayPal (decimal string, uppercase currency):
import { toSnapshot, toDecimal } from 'dinero.js';
function toPaypalMoney(dineroObject) {
const { currency } = toSnapshot(dineroObject);
return {
value: toDecimal(dineroObject),
currency_code: currency.code,
};
}
// { value: '19.99', currency_code: 'USD' }Square (BigInt amount, uppercase currency):
import { toSnapshot } from 'dinero.js';
function toSquareMoney(dineroObject) {
const { amount, currency } = toSnapshot(dineroObject);
return {
amount: BigInt(amount),
currency: currency.code,
};
}
// { amount: 1999n, currency: 'USD' }Keep these helpers in a single module (e.g., lib/money.js) so format changes only need updating in one place.
Reference: https://v2.dinerojs.com/guides/integrating-with-payment-services
Store Amount, Currency Code, and Scale as Separate Columns
Store each component of a Dinero object separately. This is portable across databases and preserves all information needed for reconstruction.
Correct (SQL schema):
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price_amount BIGINT NOT NULL,
price_currency VARCHAR(3) NOT NULL,
price_scale INTEGER NOT NULL DEFAULT 2
);Correct (restoration from database row):
import { dinero } from 'dinero.js';
import { getCurrency } from './currencies'; // Your validation helper
function dineroFromRow(row) {
const currency = getCurrency(row.price_currency);
return dinero({
amount: row.price_amount,
currency,
scale: row.price_scale,
});
}Always store the scale, not just the currency exponent. If a Dinero object was created with a custom scale (e.g., from a multiply with a scaled amount), restoring with just the exponent produces the wrong value.
Reference: https://v2.dinerojs.com/guides/storing-in-a-database
Avoid PostgreSQL's money Type for Multi-Currency Applications
PostgreSQL's money type has no currency information, is locale-dependent, and has fixed 2-decimal precision. It fails for currencies with 0 decimals (JPY), 3 decimals (BHD), or multi-currency support.
Incorrect (PostgreSQL money type):
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price MONEY NOT NULL -- No currency info, locale-dependent, fixed precision
);Correct (separate columns):
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price_amount BIGINT NOT NULL,
price_currency VARCHAR(3) NOT NULL,
price_scale INTEGER NOT NULL DEFAULT 2
);The same advice applies to other database-specific money types. Use standard integer and string columns for maximum portability and control.
Reference: https://v2.dinerojs.com/guides/storing-in-a-database
Define Custom Currencies with as const satisfies for Type Safety
When defining custom currencies (e.g., cryptocurrencies, loyalty points), use as const satisfies to get a literal type for the code property. Without it, TypeScript infers string, losing compile-time currency mismatch detection.
Incorrect (code inferred as string):
import type { Currency } from 'dinero.js';
const BTC = { code: 'BTC', base: 10, exponent: 8 };
// typeof BTC.code is string, not 'BTC'Correct (literal type with as const satisfies):
import type { Currency } from 'dinero.js';
const BTC = {
code: 'BTC',
base: 10,
exponent: 8,
} as const satisfies Currency<number, 'BTC'>;
// typeof BTC.code is 'BTC'With literal types, TypeScript catches mistakes like adding BTC and USD at compile time:
const btcAmount = dinero({ amount: 100000000, currency: BTC });
const usdAmount = dinero({ amount: 500, currency: USD });
add(btcAmount, usdAmount); // Type error: 'USD' is not assignable to 'BTC'All built-in currencies from dinero.js/currencies already have literal types.
Reference: https://v2.dinerojs.com/guides/currency-type-safety
TypeScript Catches Currency Mismatches at Compile Time
When using typed currencies, TypeScript prevents operations between different currencies. This catches bugs that would otherwise only fail at runtime.
Caught at compile time:
import { dinero, add, subtract, equal } from 'dinero.js';
import { USD, EUR } from 'dinero.js/currencies';
const price = dinero({ amount: 500, currency: USD }); // Dinero<number, 'USD'>
const tax = dinero({ amount: 100, currency: EUR }); // Dinero<number, 'EUR'>
add(price, tax); // Type error: 'EUR' is not assignable to 'USD'
subtract(price, tax); // Type error
equal(price, tax); // Type errorCurrency type preserved through operations:
import { dinero, multiply, convert } from 'dinero.js';
import { USD, EUR } from 'dinero.js/currencies';
const price = dinero({ amount: 500, currency: USD });
const doubled = multiply(price, 2); // Dinero<number, 'USD'> — preserved
const converted = convert(price, EUR, rates); // Dinero<number, 'EUR'> — changed
add(doubled, converted); // Type error: 'EUR' is not assignable to 'USD'Unary operations (multiply, allocate, trimScale) preserve the currency type. convert changes it to the target currency.
Reference: https://v2.dinerojs.com/guides/currency-type-safety
Validate Currency Codes from External Sources at Runtime
Currency codes from APIs, databases, or user input may be invalid. Always validate before looking up a currency definition.
Incorrect (direct lookup without validation):
import * as currencies from 'dinero.js/currencies';
function createPrice(amount: number, code: string) {
const currency = currencies[code]; // May be undefined
return dinero({ amount, currency }); // Runtime error
}Correct (validate before lookup):
import { dinero } from 'dinero.js';
import * as currencies from 'dinero.js/currencies';
function getCurrency(code: string) {
if (!(code in currencies)) {
throw new Error(`Unknown currency code: ${code}`);
}
return currencies[code as keyof typeof currencies];
}
function createPrice(amount: number, code: string) {
const currency = getCurrency(code);
return dinero({ amount, currency });
}Currency codes may also change between Dinero.js versions, as the library tracks ISO 4217 amendments. Pin your package version if you need stability.
Reference: https://v2.dinerojs.com/faq/how-to-look-up-a-currency-by-code