2

I have fetch() setup to retrieve historical webinar details from GotoWebinar using their API. It returns a JSON body with this structure, an array of objects:

[{ "webinarKey":5653814518315977731, "webinarID":"562189251", "subject":"Sample Webinar", "organizerKey":100000000000331530 }, { "webinarKey":9999814518315977731, "webinarID":"999989251", "subject":"Sample Webinar", "organizerKey":999900000000331530 }]

My code is being implented in a Zapier action(node.js) and the important bit looks like this:

//Handle errors from fetch call
function handleFetchStatus(response){
    console.log('Fetch Response Status: ' + response.status);

    switch(response.status){
        case 200: //Request executed properly
            break;

        default:
            throw Error(response.status + ':' + JSON.stringify(response));
    }

    return response.json();

}

function handleFetchBody(oResponse){
    if (oResponse) {
        console.log('handleFetchBody: ' + JSON.stringify(oResponse));
    }

    callback(null, oResponse);
}


//Send POST request.
fetch(getFetchURL(), getFetchOptions())
    .then(handleFetchStatus)
    .then(handleFetchBody)
    .catch(function(error) {
        callback(error);
    });

The problem I've got is that the 'webinarKey', a long number, is being truncated from "5653814518315977731" to "5653814518315978000". I believe it's the json() function that isn't handling large numbers.

How can I stop this?

I believe I need to turn the webinarKey into a string before I use json(), but I'm not sure how to access all elements of the object. Is that even possible in a fetch response.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Look Left
  • 1,305
  • 3
  • 15
  • 20

2 Answers2

3

It has to do with the number of digits of precision in JavaScript. In JavaScript, all numbers are stored as floating-point, which is like scientific notation in base 2 instead of base 10. There is a fixed number of bits (53 in this case) available to represent a in a * (2 ** b). If you look at Number.MAX_SAFE_INTEGER, the greatest integer that can be represented with 53 bits of precision, you will see that it has 16 base-10 digits. Your number has 19. If you just type the number into a JavaScript console, you will see that it prints out the rounded version (with 16 digits of precision). If you need to be storing such large numbers, it is usually best to store them as strings.

csander
  • 1,385
  • 10
  • 11
  • I understand that I can't store a number that large and that it needs to be converted to a string. But the API call returns a response body with a format that I have no control over. So how can I access the elements of the response body, so that I can convert them to a string before passing it to .json() ? – Look Left Aug 17 '17 at 04:30
  • 4
    If you parse the response as text (`response.text()` instead of `response.json()`), you can call `text.replace(/("[^"]*"\s*:\s*)(\d{16,})/g, '$1"$2"')` to replace all strings of 16 or more digits with strings. You can then pass this text into `JSON.parse()`. – csander Aug 18 '17 at 01:53
  • Thanks, that did the trick. I was hoping not to do string parsing that but it appears to be the only way. – Look Left Aug 18 '17 at 04:25
0

This question is really old but came up when I searched for this problem, so I figured I'd leave an answer here. I had a similar problem (external API I had no control over returning very large integer values as IDs) which were getting truncated by json.parse. I ended up using this lossless-json NPM package to solve the problem with a custom number parser that translates large integer values to strings. This was sufficient for me because I just needed the reference as an ID.

customNumberParser(value: any) {
  if (value.isLosslessNumber) {
    return value.toString();
  }
  return value;
}
Tyler2P
  • 2,324
  • 26
  • 22
  • 31