In a recent project, we had to build a realtime analytics dashboard, for visualizing traffic data from various sources.
Each source is identified by an ID. These IDs are rather big numbers, think of them like an IPv6 address.
I would have never anticipated what we would encounter next.
Big numbers (anything that needs more than 8 bytes) are generally not a big (pun intended) problem in most programming languages.
In most…
Enter JavaScript
This project sends the entire sensor data into Tinybird, a really cool product. It is basically a hosted Clickhouse database. So, built for analytics data!
We collect the data, process it a little bit and then send it off to an endpoint of the Tinybird REST API, as JSON.
The one requirement in this: the processing part has to be in JavaScript (Node LTS).
And this is where things become,… tricky.
JSON can't handle it
In recent years, JS has received native support for arbitrarily large numbers. Regular numbers are Number
and anything that doesn't fit into 64 bit is BigInt
.
So my assumption was: when we send or receive a huge number, JS should be able to handle that correctly.
It does not.
If we receive:
{"source": 55878094393440482840222217111980736512}
and I put that through a JSON.parse
> JSON.parse('{"source": 55878094393440482840222217111980736512}')
{ source: 5.587809439344048e+37 }
We can see that the native JSON parse throws away everything after 5587809439344048
. (I tried that in browser, node and deno)
stringify
also lacks support for this:
// The "n" at the end here signals to the interpreter to use BigInt:
> typeof 55878094393440482840222217111980736512n
'bigint'
> JSON.stringify({source: 55878094393440482840222217111980736512n})
Uncaught TypeError: Do not know how to serialize a BigInt
at JSON.stringify (<anonymous>)
I am very sure there are good reasons for this. Nevertheless, this is incredibly frustrating and a source of errors if not properly verified.
The solution
Thankfully, the JavaScript ecosystem has come up with workarounds for this. In typical JS fashion, there are quite a few packages that get the job done.
I settled our problem with safe-stable-stringify and lossless-json (for parsing) because I liked the download counts and benchmarks.
> const stringify = require("safe-stable-stringify")
> stringify({source: 55878094393440482840222217111980736512n})
'{"source":55878094393440482840222217111980736512}'
This right there is proof why you need to write tests. Without them, this would have slipped through.