Shalitha Suranga Programmer | Author of Neutralino.js and Jerverless

Store and retrieve precise monetary values in JavaScript with Dinero.js

6 min read 1818

Storing and retrieving precise monetary values in JavaScript with Dinero.js

Programmers work with various sorts of web applications belonging to different areas of business, such as healthcare, finance, ecommerce, cloud computing, business processes management, and education. These industries often manipulate monetary values, and as more and more financial businesses convert their services into cloud-based applications, it is becoming apparent that these cloud-based apps need to be capable of representing, manipulating, and storing financial amounts precisely.

Even though your web application may not explicitly exist to perform financial calculations, it may calculate monetary amounts for billing purposes. These types of monetary calculations should be more accurate because errors can result in real-world losses for the parties involved in those financial transactions.

Therefore, we need to select the most suitable data type to store financial numbers. Several programming languages such as C# and Java natively support monetary values with arbitrary point decimal types. However, JavaScript’s hardware-based float data type is not suitable for monetary applications. It doesn’t have a native data type for monetary values, and it only lets you store decimals as double-precision floats (IEEE 754).

In this article, I will explain how to store, manipulate, and represent monetary values more precisely in JavaScript and TypeScript with objects using the Dinero.js library.

Why is the float data type bad for storing monetary values?

In general, people use the base-ten number system for their calculations. The base-10 number system uses digits from 0 to 9. Therefore, all numbers we calculate are made out of these or a combination of these digits.

Our number calculations often produce exact decimal numbers and can produce endless fractional numbers, too. For example, the 1/3 fraction creates an endless 0.3333…. On the other hand, the 1/4 fraction creates the exact 0.25.

Computers, as we know, can only understand a base-two number system known as the binary number system, which consists of a higher voltage value (1) and a lower voltage value (0). The computers will store decimal values as base-two numbers internally, and as with the base-10 system, the decimal-to-binary conversion can create endless binary fractions, too.

For example, the 1/10 decimal fraction makes an endless 0.00011… binary number. On the other hand, the 1/2 decimal fraction creates the exact 0.1 binary number.

As you can probably tell by now, the float data type stores an approximate value of the given decimal value when the binary representation is too long or endless. When you retrieve the stored float value for calculations, it’s not the exact value you stored earlier.

A meme explaining the float data type conversion error

We made a custom demo for .
No really. Click here to check it out.

These conversion errors may generate considerable gaps in aggregated amounts, and can result in rounding errors. Therefore, the native JavaScript float data type is not suitable for monetary values.

Getting started with Dinero.js

Dinero.js is built based on Martin Fowler’s money pattern, which is explained in his book Patterns of Enterprise Application Architecture. The book explains that software developers need to store monetary values with an OOP (object-oriented programming) pattern. The pattern motivates developers to use a class with currency and amount for storing monetary values.

As a result, Dinero.js lets programmers store, manipulate, and present monetary values precisely in JavaScript using objects. It supports definitions for all active currencies in the world, including nondecimal currencies like Malagasy ariary, and comes as a set of isolated modules, so that you can include only the required modules in the production application to keep the bundle size smaller.

Dinero’s v2 alpha version was recently released, and it will become the stable version soon. In the meantime, you can use Dinero with Node.js, your browser, and with all popular frontend frameworks.

To get started, install the library with npm or the Yarn package manager.

npm install [email protected]
# or
yarn add [email protected]

Alternatively, you can import a minified version of Dinero to use directly inside the web browser. In this tutorial, I will demonstrate Dinero with Node by using an ECMAScript module (MJS).

Basic concepts

First, let’s try to store a value of 75.50 with Dinero. Assume that this value comes with an unknown currency. Save the following code to a file named index.mjs and run it with the node index.mjs command.

import { dinero } from 'dinero.js';
const price = dinero({amount: 7550, scale: 2});
console.log(price.toJSON());

We need to understand that we are not dealing with floating-point numbers or integer values. Every monetary amount will become a Dinero object with the Dinero API.

The above snippet creates a Dinero object with 7550 because we need to provide an integer as the amount. We also ask Dinero to use a scale factor of 2 because the actual price was 75.50. The last line logs the Dinero object’s structure, as shown below.

Dinero's object structure

Currencies

We created the above Dinero object without any currency. As mentioned earlier, the library supports almost all active currencies in the world, but for the sake of ease, let’s try to store 75.99 USD.

Before using the predefined currencies, you need to install the Dinero currencies package first.

npm install @dinero.js/[email protected]
# or  
yarn add @dinero.js/[email protected]

After installing the currencies package, run the following code snippet.

import { dinero } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const price = dinero({amount: 7599, currency: USD})
console.log(price.toJSON());

Now, we can see that the scale is automatically defined based on the currency’s exponent property.

The scale is automatically defined by the currency's exponent property

Formatting

We can use Dinero objects for internal monetary representations, but we need to display them in plain text in our web applications. So, we need to convert Dinero objects to typical decimal numbers by using the library’s formatting functions.

Look at how the following Dinero object is converted into a decimal value with one decimal place.

import { dinero, toUnit, down } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const price = dinero({amount: 7599, currency: USD});
const priceDecimal = toUnit(price, { digits: 1, round: down });
console.log(priceDecimal); // 75.9

Moreover, you can get a string representation of decimal values with the toFormat function, as shown below.

import { dinero, toFormat } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const transformer = ({ amount, currency }) => `${currency.code} ${amount}`;
const price = dinero({ amount: 7599, currency: USD });
console.log(toFormat(price, transformer)); // "USD 75.99"

Storing and retrieving

Dinero objects are stored in the computer’s physical memory like any other generic JavaScript object. Obviously, we need to save them into databases for long-term persistence.

Dinero offers API functions for serialization and deserialization, and we can use the toSnapshot function to get a storable raw JSON object.

import { dinero, toSnapshot } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const price = dinero({ amount: 5000, currency: USD });
const rawData = toSnapshot(price);
console.log(rawData); // Store rawData in database

We can convert the stored raw JSON object back to a Dinero object with the dinero method as usual.

import { dinero, toSnapshot } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const price = dinero({ amount: 5000, currency: USD });
const rawData = toSnapshot(price); 
// Store rawData in database
const priceFromDb = dinero(rawData);
console.log(priceFromDb.toJSON());

Arithmetic operations

Dinero has several API mutation functions that allow you to manipulate monetary amounts that are defined inside Dinero objects. With these functions, we can perform arithmetic operations such as add, multiply, and subtract. Look at the following code snippet for some sample arithmetic operations.

import { dinero,
        toUnit,
        down,
        add,
        subtract, 
        multiply 
} from 'dinero.js';
import { USD } from '@dinero.js/currencies';

const d1 = dinero({amount: 7599, currency: USD});
const d2 = dinero({amount: 1599, currency: USD});
console.log(`d1 = ${toUnit(d1)}`);
console.log(`d2 = ${toUnit(d1)}`);

let ans;

ans = add(d1, d2);
console.log(`d1 + d2 = ${toUnit(ans)}`);

ans = subtract(d1, d2);
console.log(`d1 - d2 = ${toUnit(ans)}`);

ans = multiply(d1, 2);
console.log(`d1 x 2 = ${toUnit(ans)}`);

Monetary division

We often need to divide monetary amounts in real-world transactions. In some scenarios, these divisions can be a bit more complex. For example, assume that your web application processes a total payment of USD 100.53 across two transactions for one specific service. You may guess that there will be two identical payments of USD 50.265. But you can’t divide a physical penny into two parts, can you?

Even though you can round up this value to USD 50.27, you are charging more than the actual value. To solve this, Dinero offers the allocate method to distribute monetary values without any division errors.

The following example distributes USD 100.53 into two amounts.

import { dinero, allocate, toUnit } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const price = dinero({ amount: 10053, currency: USD });
const [d1, d2] = allocate(price, [1, 1]);
console.log(toUnit(d1), toUnit(d2)); // 50.27 50.26 

The above code snippet creates two Dinero objects: one with 50.27 and one with 50.26. Therefore, the division results in two amounts that correctly translate to currency.

Arithmetic comparisons

Dinero has many functions for comparison purposes. For example, we can use the equal method to check the equality of two Dinero objects.

import { dinero, equal } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const d1 = dinero({ amount: 5022, currency: USD });
const d2 = dinero({ amount: 5022, currency: USD });
if(equal(d1, d2)) {
   console.log("d1 equals d2");
}

Likewise, there are API functions to check greater than, less than, etc. The Dinero API documentation has definitions for all supported comparison functions.

TypeScript support

Dinero.js is written in TypeScript, and it natively supports TypeScript development environments.

Add Dinero as a dependency into your TypeScript project. After that, your package manager will fetch all TypeScript definitions, too.

Look at the following Dinero TypeScript example.

import { Dinero, dinero, toUnit, down } from 'dinero.js';
import { USD } from '@dinero.js/currencies';
const price: Dinero = dinero({ amount: 50011, currency: USD });
const priceDecimal: number = toUnit(price, { digits: 2, round: down });
console.log(priceDecimal);

You can check the complete source of the above example from this StackBlitz demo.

Dinero.js vs. the other existing solutions

Dinero.js was initially released about a year ago. Before then, programmers used different Dinero-like libraries to represent monetary amounts in JavaScript. Some programmers used other approaches without using libraries, like Martin Fowler’s money pattern, which stores monetary values as integers, but they then needed to implement everything from scratch. The Money$afe library offers a similar API to Dinero, but it doesn’t implement predefined currencies, and the documentation says it’s not yet production-ready.

big.js is another popular library for manipulating arbitrary-precision decimal numbers. However, its solution to storing decimal values without rounding errors is kind of generic, and we can’t expect many money-related features, such as predefined currencies or equal division. However, big.js is a good solution for dealing with cryptocurrencies — even Dinero recommends big.js for cryptocurrency on their FAQ page.

Conclusion

Programmers always need to use the correct data type to store financial values. Otherwise, web applications may store, process, and display incorrect financial amounts, and web application owners may face customer dissatisfaction issues, or even legal action, as a result.

Luckily, Dinero is a JavaScript library that provides a safe domain model for monetary manipulation in JavaScript with a modular and immutable API.

Are you adding new JS libraries to improve performance or build new features? What if they’re doing the opposite?

There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.

LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.

https://logrocket.com/signup/

LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Build confidently — .

Shalitha Suranga Programmer | Author of Neutralino.js and Jerverless

Testing accessibility with Storybook

One big challenge when building a component library is prioritizing accessibility. Accessibility is usually seen as one of those “nice-to-have” features, and unfortunately, we’re...
Laura Carballo
4 min read

Leave a Reply