Handling Large Numbers in HTTP Requests: Precision Loss Issue

Hi everyone,

I’ve encountered an issue with handling large numbers in HTTP requests using the HTTP Request node in n8n. Specifically, I’ve noticed that large numbers are being altered due to precision loss. For instance, a number like 997620301473206272 gets changed to 997620301473206300, which causes problems in my workflow.

The Problem:

When sending HTTP requests, any numeric value larger than Number.MAX_SAFE_INTEGER (which is 9007199254740991) loses precision. This has led to incorrect data being sent in my requests, which is particularly problematic for IDs or other critical large number values.

Steps I’ve Taken:

To address this, I’ve looked into the HTTP Request node’s code and found a potential solution. I’ve added a function that converts numbers larger than Number.MAX_SAFE_INTEGER to strings before sending the request. This ensures that the numbers are not altered due to precision issues.

Proposed Solution:

Here’s a brief overview of the changes I made:

  1. Added a Function to Convert Large Numbers to Strings: The function convertNumbersToStrings recursively traverses the data and converts any number exceeding Number.MAX_SAFE_INTEGER into a string.
  2. Integrated the Function in the Request Preparation: I updated the execute method in the HttpRequestV3 class to call the convertNumbersToStrings function before setting the requestOptions.body.
function convertNumbersToStrings(data: any): any {
    if (typeof data === 'number' && data > Number.MAX_SAFE_INTEGER) {
        return data.toString();
    } else if (Array.isArray(data)) {
        return data.map(convertNumbersToStrings);
    } else if (typeof data === 'object' && data !== null) {
        return Object.fromEntries(
            Object.entries(data).map(([key, value]) => [key, convertNumbersToStrings(value)])
        );
    }
    return data;
}

I then used this function within the execute method to ensure all large numbers are converted before the request is sent.

export class HttpRequestV3 implements INodeType {
    // ...
    async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
        const items = this.getInputData();
        const nodeVersion = this.getNode().typeVersion;

        // ...
        for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
            // ...
            const sendBody = this.getNodeParameter('sendBody', itemIndex, false) as boolean;
            const bodyContentType = this.getNodeParameter('contentType', itemIndex, '') as string;
            const specifyBody = this.getNodeParameter('specifyBody', itemIndex, '') as string;
            const bodyParameters = this.getNodeParameter('bodyParameters.parameters', itemIndex, []) as BodyParameter[];
            const jsonBodyParameter = this.getNodeParameter('jsonBody', itemIndex, '') as string;
            const body = this.getNodeParameter('body', itemIndex, '') as string;

            // Get parameters defined in the UI
            if (sendBody && bodyParameters) {
                if (specifyBody === 'keypair' || bodyContentType === 'multipart-form-data') {
                    let preparedBody = await prepareRequestBody(bodyParameters, bodyContentType, nodeVersion, parametersToKeyValue);
                    // Convert large numbers to strings
                    preparedBody = convertNumbersToStrings(preparedBody);
                    requestOptions.body = preparedBody;
                } else if (specifyBody === 'json') {
                    if (typeof jsonBodyParameter !== 'object' && jsonBodyParameter !== null) {
                        try {
                            JSON.parse(jsonBodyParameter);
                        } catch {
                            throw new NodeOperationError(this.getNode(), 'JSON parameter need to be an valid JSON', { itemIndex });
                        }
                        let parsedJsonBody = jsonParse(jsonBodyParameter);
                        parsedJsonBody = convertNumbersToStrings(parsedJsonBody);
                        requestOptions.body = parsedJsonBody;
                    } else {
                        let jsonBody = jsonBodyParameter;
                        jsonBody = convertNumbersToStrings(jsonBody);
                        requestOptions.body = jsonBody;
                    }
                } else if (specifyBody === 'string') {
                    let urlencodedBody = Object.fromEntries(new URLSearchParams(body));
                    urlencodedBody = convertNumbersToStrings(urlencodedBody);
                    requestOptions.body = urlencodedBody;
                }
            }
            // ...
        }
        // ...
    }
}

Seeking Feedback and Further Suggestions:

I’m sharing this here to see if anyone else has faced similar issues and to get feedback on this solution. If there’s a more efficient way to handle this within n8n or if there are any potential pitfalls with this approach, I would greatly appreciate your insights.

Thank you!

2 Likes