APIs with 'long' data
JSON schema supports integer data type and int64 format. And because OpenAPI integrates JSON schema, you may come across an API definition with an int64 property in the request or response.
For programming languages like .Net and Java, int64 (or ‘long’) numbers have a maximum value of 2^63-1 (9,223,372,036,854,775,807).
Javascript Numbers
In contrast, numbers processed in Javascript have a maximum “safe” value of 2^53-1 (9,007,199,254,740,991).
int64 max: 9,223,372,036,854,775,807
js safe: 9,007,199,254,740,991
Why does Javascript not support the full integer range of the int64 (long) data type, and what is meant by a “safe” value?
For JavaScript software following the IEEE 754 standard (which apparently includes most web browsers and JS dev frameworks), numbers are always stored as double precision floating point numbers. This fact sets the maximum precision to 53 binary digits, which sets the maximum value to 2^53-1 (9,007,199,254,740,991).
Numbers greater than this value start to loose precision in Javascript, and calculations become unreliable, giving strange results such as:
9007199254740992 === 9007199254740993
So MAX_SAFE_INTEGER (2^53-1 or 9,007,199,254,740,991) is the largest integer value that you can safely use in Javascript, before results become unreliable.
What then are the implications for your APIs?
API Interoperability
Handling numbers greater than MAX_SAFE _INTEGER is not standardized, and so Javascript clients can process such numbers in different ways.
For example, if your API returns an integer value 9,007,199,254,740,992 (MAX_SAFE_INTEGER + 1), one JS client might decode the value to 9,007,199,254,740,991 (MAX_SAFE_INTEGER), another might decode it to 0, another might throw an error.
Your API loses interoperability!
API Security
In addition to interoperability issues, exchanging integer values larger than MAX_SAFE_INTEGER can also create vulnerabilities.
Consider for example an API request processed by multiple backend services in a chain, one Java and one Javascript. The incoming request contains an account ID that uses int64 format. The Java component might process the request to check for authorization on the account ID, while the Javascript component processes the business request on the account (e.g. get balance).
Now, if the account ID value exceeds MAX_SAFE_INTEGER (e.g. 9007199254740992), the Java component has no problem with that, and will authorize the correct account ID.
However, the Javascript account may default the value back to MAX_SAFE_INTEGER (9007199254740991), and now it’s processing a request for the wrong account ID.
I’m not sure how realistic this particular hypothetical is, but clearly when two server components are out of sync it creates a vulnerability that hackers may find ways to exploit (ref. OWASP API8:2023).
Solutions
If your goal is to achieve API interoperability with the majority of Javascript clients, and to prevent vulnerabilities, here are two alternative approaches to consider:
Limit your API integer properties (request or response) to a maximum value of MAX_SAFE_INTEGER.
Document it so clients can see the acceptable limits, and enforce it in your own API code to prevent unexpected results and vulnerabilities.
Alternatively, if your API must support integer values greater than MAX_SAFE_INTEGER, you can consider sending the values as string type instead of integer type.
For example Google API appear to be using this approach for int64 values.