System Boundaries
Money usually crosses several boundaries. The safest shape depends on where the value is crossing.
money-safe focuses on amount shape: parsing, integer minor units, arithmetic, allocation, formatting, and serialized payloads. It does not own exchange rates, accounting ledgers, tax policy, provider orchestration, or product pricing rules.
Browser and public API input
Use decimal strings:
{
"amount": "19.99",
"currency": "CNY"
}const value = amount(payload.amount, payload.currency);Database and payment providers
Use minor units:
amount_minor BIGINT NOT NULL,
currency CHAR(3) NOT NULL,
minor_unit SMALLINT NOT NULLconst value = amount.minor(row.amount_minor, row.currency, {
minorUnit: row.minor_unit
});Service-to-service payloads
Use serialized money:
{
"amountMinor": "1999",
"currency": "CNY",
"minorUnit": 2
}const value = amount.fromJSON(payload);amountMinor is a string so large values can move across JSON without passing through JavaScript Number.
Display
Format only at display boundaries:
amount('19.99', 'CNY').format('zh-CN');Do not feed formatted strings back into business logic.
Business policy stays in your app
Keep policy decisions next to the business flow that owns them:
- Which tax rate applies.
- Which payment provider receives the payload.
- Which ledger account records the movement.
- Which exchange rate source is trusted.
Use money-safe for the money value that crosses those boundaries, not as the owner of those policies.