What JSON.parse does to a 20-digit integer, and how to keep the digits
If an API sends you 12345678901234567890 as a JSON number, what does JSON.parse give you? I ran a few cases in Node v22.23.4 (V8) to see exactly where the digits go, and what the newer context.source and JSON.rawJSON options do about it. What parse and stringify do to big integers const s = ' {"id":12345678901234567890,"small":9007199254740993,"safe":9007199254740991,"price":0.1,"f":1.0} ' ;…
When an API sends you a 20-digit integer as a JSON number, what does JSON.parse give you? A test was conducted in Node v22.23.4 (V8) to observe the behavior of JSON.parse and its newer options, context.source and JSON.rawJSON. Here is what the experiment revealed:
1. After JSON.parse, the field containing the 20-digit integer shows as 12345678901234567000, which is incorrect. The number 9007199254740993 (2^53 + 1) becomes 9007199254740992 (2^53), and the safe integer 9007199254740991 (2^53 - 1) remains unchanged at 9007199254740991. A floating-point number 1.0 converts to 1 after a round trip.
2. An interesting observation is that two different IDs can collapse into one due to the alteration of digits. No error or warning is thrown during this process, and JSON.stringify(o) writes the changed digits back out.
3. The reviver feature in JSON.parse provides a third argument containing the original text, allowing for safe conversion of unsafe integers into BigInt. When applied to the test object, the results show that 'id' and 'small' are now BigInts (12345678901234567890 and 9007199254740993, respectively), while 'price' remains a number (0.1).
4. However, converting BigInt back to a number using JSON.stringify(r) throws a TypeError, indicating that there is no direct way to serialize a BigInt at this time. JSON.rawJSON() is introduced as an alternative to write an exact number literal without losing precision. The MDN documentation provides examples on both context.source and JSON.rawJSON().
Based on these findings, the recommended approach is to request the API to send big integers as strings whenever possible. Alternatively, if changing the API is not an option, read the data using context.source in a runtime that supports it, and maintain the data as a string or BigInt rather than a number. Avoid performing arithmetic operations on IDs and compare them as strings instead.
Conduct a test with the smallest value that fails, which is 9007199254740993. Keep in mind that the results may vary across different runtimes, browsers, and JSON libraries. It is essential to verify your specific runtime before relying on the reviver option.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.