AWS Lambda: How To Access POST Parameters (NodeJS)

This article will cover how to access AWS Lambda HTTP POST Request parameters in the following situations:

  1. Simple HTML form with “action” URL set to an API Gateway URL connected to a AWS Lambda function
  2. An HTML form with file upload with the “action” URL set to an API Gateway URL connected to a AWS Lambda function
  3. AJAX POST request via on page JavaScript

Lets get started…

Situation 1: HTML Form POST With Action URL As API Gateway URL Connected To AWS Lambda Function

Here we have a simple HTML form that creates the post request. Maybe the code for this form is something like this…

<form method="POST" action="https://some_api_gateway_url.execute-api.ap-south-1.amazonaws.com/default/postParamsDemo">

    <label>Your First Name</label>
    <input type="text" name="first_name" />

    <label>Your Last Name</label>
    <input type="text" name="last_name" />

    <label>Message</label>
    <textarea name="message"></textarea>

    <input type="submit" />

</form>

On the AWS Lambda side, I have created a very simple function that just echos the “event” data that is sent to the function..

AWS Lambda simple code to just echo the event in Node JS

Here is the code if you want to copy paste it..

exports.handler = async (event) => {

    const response = {
        statusCode: 200,
        headers: {"content-type": "application/json"},
        body: JSON.stringify(event),
    };
    return response;

};

Lets look at the RAW Event Data..

Below is the “raw event data” we get from API Gateway. Now this is going to be long. But I have pasted the whole thing here so that you can look at this in order to see the all information you get..

{
  "version": "1.0",
  "resource": "/postParamsDemo",
  "path": "/default/postParamsDemo",
  "httpMethod": "POST",
  "headers": {
    "Content-Length": "131",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "e5p88is0ub.execute-api.ap-south-1.amazonaws.com",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36",
    "X-Amzn-Trace-Id": "Root=1-62f4601d-2add60115a12c64034e702d3",
    "X-Forwarded-For": "152.57.217.49",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-GB;q=0.7",
    "cache-control": "max-age=0",
    "origin": "http://127.0.0.1:5500",
    "referer": "http://127.0.0.1:5500/",
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "cross-site",
    "sec-fetch-user": "?1",
    "sec-gpc": "1",
    "upgrade-insecure-requests": "1"
  },
  "multiValueHeaders": {
    "Content-Length": [
      "131"
    ],
    "Content-Type": [
      "application/x-www-form-urlencoded"
    ],
    "Host": [
      "e5p88is0ub.execute-api.ap-south-1.amazonaws.com"
    ],
    "User-Agent": [
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36"
    ],
    "X-Amzn-Trace-Id": [
      "Root=1-62f4601d-2add60115a12c647034e702d3"
    ],
    "X-Forwarded-For": [
      "152.57.217.42"
    ],
    "X-Forwarded-Port": [
      "443"
    ],
    "X-Forwarded-Proto": [
      "https"
    ],
    "accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
    ],
    "accept-encoding": [
      "gzip, deflate, br"
    ],
    "accept-language": [
      "en-GB;q=0.7"
    ],
    "cache-control": [
      "max-age=0"
    ],
    "origin": [
      "http://127.0.0.1:5500"
    ],
    "referer": [
      "http://127.0.0.1:5500/"
    ],
    "sec-fetch-dest": [
      "document"
    ],
    "sec-fetch-mode": [
      "navigate"
    ],
    "sec-fetch-site": [
      "cross-site"
    ],
    "sec-fetch-user": [
      "?1"
    ],
    "sec-gpc": [
      "1"
    ],
    "upgrade-insecure-requests": [
      "1"
    ]
  },
  "queryStringParameters": null,
  "multiValueQueryStringParameters": null,
  "requestContext": {
    "accountId": "3892943",
    "apiId": "e5p88is0ub",
    "domainName": "e5p88is0ub.execute-api.ap-south-1.amazonaws.com",
    "domainPrefix": "e5p88is0ub",
    "extendedRequestId": "WrP0nju8hcwEJew=",
    "httpMethod": "POST",
    "identity": {
      "accessKey": null,
      "accountId": null,
      "caller": null,
      "cognitoAmr": null,
      "cognitoAuthenticationProvider": null,
      "cognitoAuthenticationType": null,
      "cognitoIdentityId": null,
      "cognitoIdentityPoolId": null,
      "principalOrgId": null,
      "sourceIp": "152.57.217.42",
      "user": null,
      "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36",
      "userArn": null
    },
    "path": "/default/postParamsDemo",
    "protocol": "HTTP/1.1",
    "requestId": "WrP0nju8hcwEJew=",
    "requestTime": "11/Aug/2022:01:49:17 +0000",
    "requestTimeEpoch": 1660182557324,
    "resourceId": "ANY /postParamsDemo",
    "resourcePath": "/postParamsDemo",
    "stage": "default"
  },
  "pathParameters": null,
  "stageVariables": null,
  "body": "Zmlyc3RfbmFtZT1MaXZlZmlyZSZsYXN0X25hbWU9ZGV2Jm1lc3NhZ2U9aGlzK3RoaXMraXMrYSttZXNzYWdlK2Zyb20rdGhlK2NvbnRhY3QrZm9ybSUyMSUwRCUwQSUwRCUwQUl0K2hhcytuZXcrbGluZXMraW4raXQlMjE=",
  "isBase64Encoded": true
}

As you can see, there is a lot of information in there. What you care about are the post params.

The Part With The POST Params

"body": "Zmlyc3RfbmFtZT1MaXZlZmlyZSZsYXN0X25hbWU9ZGV2Jm1lc3NhZ2U9aGlzK3RoaXMraXMrYSttZXNzYWdlK2Zyb20rdGhlK2NvbnRhY3QrZm9ybSUyMSUwRCUwQSUwRCUwQUl0K2hhcytuZXcrbGluZXMraW4raXQlMjE=",
"isBase64Encoded": true

As you can see, the POST data is “base64 encoded“.

If you use a base64 decoder like this one: you will see that the data is actually something like:

first_name=Livefire&last_name=dev&message=his+this+is+a+message+from+the+contact+form%21%0D%0A%0D%0AIt+has+new+lines+in+it%21

Okay, so it looks like all the params are shushed together like query string parameters. And the values are URL encoded.

Some Helper Functions To The Rescue

I have written some simple functions to decode the data and turn it into a nice and easy to use JS Object. Below are the functions being used in a node js script..

let input = "Zmlyc3RfbmFtZT1MaXZlZmlyZSZsYXN0X25hbWU9ZGV2Jm1lc3NhZ2U9aGlzK3RoaXMraXMrYSttZXNzYWdlK2Zyb20rdGhlK2NvbnRhY3QrZm9ybSUyMSUwRCUwQSUwRCUwQUl0K2hhcytuZXcrbGluZXMraW4raXQlMjE=";

let deCodeURLVal = (str) => {
    return decodeURIComponent((str+'').replace(/\+/g, '%20'));
}

let getParamsFromBase64EncodedPOSTParams = (input) => {

    let output = {};

    // Decode the base64
    let decoded = atob(input);
    
    // Split the params by "&"
    let params = decoded.split("&");

    // Turn the params into an object and url decode the values
    params.forEach((keyAndValue) => {
        let keyValueArray = keyAndValue.split("=");
        let key = keyValueArray[0];
        let value = keyValueArray[1];
        output[key] = deCodeURLVal(value);
    });

    return output;
}

console.log(getParamsFromBase64EncodedPOSTParams(input));

The result you get is something like this..

{
  first_name: 'Livefire',
  last_name: 'dev',
  message: 'his this is a message from the contact form!\r\n\r\nIt has new lines in it!'
}

Situation 2: HTML Form POST (With File Upload) With Action URL As API Gateway URL Connected To AWS Lambda Function

Lets assume you have a HTML form with file upload. Something like this..

<form 
    method="POST" 
    action="https://some_api_gateway_url.execute-api.ap-south-1.amazonaws.com/default/postParamsDemo"
    enctype="multipart/form-data">

    <label>Your First Name</label>
    <input type="text" name="first_name" />
    
    <label>Your Last Name</label>
    <input type="text" name="last_name" />
    
    <label>Screenshot</label>
    <input type="file" name="file" />
    
    <label>Message</label>
    <textarea name="message"></textarea>
    
    <input type="submit" />

</form>

Please Notice: You have to set enctype=”multipart/form-data” if you are going to upload files using a HTML Form.

Lets look at the RAW Event Data..

If have left the AWS Lamda function the same. And in order to test this, I have selected and uploaded a tiny PNG image. Below is the RAW response..

{
  "version": "1.0",
  "resource": "/postParamsDemo",
  "path": "/default/postParamsDemo",
  "httpMethod": "POST",
  "headers": {
    "Content-Length": "3093",
    "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryLhd9dYahIiwQhBJc",
    "Host": "e5p88is0ub.execute-api.ap-south-1.amazonaws.com",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36",
    "X-Amzn-Trace-Id": "Root=1-62f501f2-36f6a02732ef919c189b19ad",
    "X-Forwarded-For": "152.57.214.6",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-GB;q=0.8",
    "cache-control": "max-age=0",
    "origin": "http://127.0.0.1:5500",
    "referer": "http://127.0.0.1:5500/",
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "cross-site",
    "sec-fetch-user": "?1",
    "sec-gpc": "1",
    "upgrade-insecure-requests": "1"
  },
  "multiValueHeaders": {
    "Content-Length": [
      "3093"
    ],
    "Content-Type": [
      "multipart/form-data; boundary=----WebKitFormBoundaryLhd9dYahIiwQhBJc"
    ],
    "Host": [
      "e5p88is0ub.execute-api.ap-south-1.amazonaws.com"
    ],
    "User-Agent": [
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36"
    ],
    "X-Amzn-Trace-Id": [
      "Root=1-62f501f2-36f6a02732ef919c189b19ad"
    ],
    "X-Forwarded-For": [
      "152.57.214.6"
    ],
    "X-Forwarded-Port": [
      "443"
    ],
    "X-Forwarded-Proto": [
      "https"
    ],
    "accept": [
      "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
    ],
    "accept-encoding": [
      "gzip, deflate, br"
    ],
    "accept-language": [
      "en-GB;q=0.8"
    ],
    "cache-control": [
      "max-age=0"
    ],
    "origin": [
      "http://127.0.0.1:5500"
    ],
    "referer": [
      "http://127.0.0.1:5500/"
    ],
    "sec-fetch-dest": [
      "document"
    ],
    "sec-fetch-mode": [
      "navigate"
    ],
    "sec-fetch-site": [
      "cross-site"
    ],
    "sec-fetch-user": [
      "?1"
    ],
    "sec-gpc": [
      "1"
    ],
    "upgrade-insecure-requests": [
      "1"
    ]
  },
  "queryStringParameters": null,
  "multiValueQueryStringParameters": null,
  "requestContext": {
    "accountId": "383392943",
    "apiId": "e5p88is0ub",
    "domainName": "e5p88is0ub.execute-api.ap-south-1.amazonaws.com",
    "domainPrefix": "e5p88is0ub",
    "extendedRequestId": "Ws093hJlBcwEJRw=",
    "httpMethod": "POST",
    "identity": {
      "accessKey": null,
      "accountId": null,
      "caller": null,
      "cognitoAmr": null,
      "cognitoAuthenticationProvider": null,
      "cognitoAuthenticationType": null,
      "cognitoIdentityId": null,
      "cognitoIdentityPoolId": null,
      "principalOrgId": null,
      "sourceIp": "152.57.214.6",
      "user": null,
      "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36",
      "userArn": null
    },
    "path": "/default/postParamsDemo",
    "protocol": "HTTP/1.1",
    "requestId": "Ws093hJlBcwEJRw=",
    "requestTime": "11/Aug/2022:13:19:46 +0000",
    "requestTimeEpoch": 1660223986164,
    "resourceId": "ANY /postParamsDemo",
    "resourcePath": "/postParamsDemo",
    "stage": "default"
  },
  "pathParameters": null,
  "stageVariables": null,
  "body": "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",
  "isBase64Encoded": true
}

So it looks like, all the form data was again base64 encoded. The file we uploaded is also in there somewhere.

If you try to decode the base64 data, you get something like..

Decoding the base64 file upload data that we get via AWS Lambda

If you look at how the browser constructs HTTP POST requests, you will see that this is the format used in the case of multipart/form-data

So, how do we parse this?

This is going to be more than just a few lines of code. So, I have created a little node module you can use.

const fs = require('fs');

function getValueIgnoringKeyCase(object, key) {
    const foundKey = Object
        .keys(object)
        .find(currentKey => currentKey.toLocaleLowerCase() === key.toLowerCase());
    return object[foundKey];
}

function getBoundary(event) {
    return getValueIgnoringKeyCase(event.headers, 'Content-Type').split('=')[1];
}

function getFilePath(event, fileName) {
    return `/tmp/${Date.now()}-${fileName}`;
}

function saveFile(buffer, filePath) {
    fs.writeFileSync(filePath, buffer);
}

function multiPartParser(event) {
    const boundary = getBoundary(event);
    const result = {};
    event.body
        .split(boundary)
        .forEach(item => {
            if (/filename=".+"/g.test(item)) {

                let fileContents = item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4);
                let fileBuffer = Buffer.from(fileContents, 'binary');
                
                let fileName = item.match(/filename=".+"/g)[0].slice(10, -1);
                let filePath = getFilePath(event, fileName);
                saveFile(fileBuffer, filePath);

                result[item.match(/name=".+";/g)[0].slice(6, -2)] = {
                    filename: fileName,
                    contentType: item.match(/Content-Type:\s.+/g)[0].slice(14),
                    path: filePath
                }

            } else if (/name=".+"/g.test(item)){
                result[item.match(/name=".+"/g)[0].slice(6, -1)] = item.slice(item.search(/name=".+"/g) + item.match(/name=".+"/g)[0].length + 4, -4);
            }
        });
    return result;
};

module.exports.parse = (event) => {

    let clonedEvent = Object.assign({}, event);
 
    if (event.isBase64Encoded) {
        let body = clonedEvent.body;
        let decodedFromBase64 = Buffer.from(body, 'base64');
        clonedEvent.body = decodedFromBase64.toString('latin1');   
    }
    let result = multiPartParser(clonedEvent);
    
    return result;
}

The above module takes the raw event data as input. It decodes and parses it. And gives back a nice JS Object with the params.

It will also take the file data and store the file in the “tmp” folder. It will give you back a path to the file.

How To Use This Module

Create a new file in the AWS Lambda Code section called “multipart_form_data_parser.js” and paste in the code from above.

AWS Lambda With Multi Part From Data Parser

You can then call the module from the main AWS Lambda JS function like so..

const multiPartParser = require('./multipart_form_data_parser');

exports.handler = async (event) => {
    
    let postParams = multiPartParser.parse(event);
    
    // TODO implement
    const response = {
        statusCode: 200,
        headers: {"content-type": "application/json"},
        body: JSON.stringify(postParams),
    };
    return response;
};

The response from the AWS function would be something like..

{
  "first_name": "Livefire",
  "last_name": "dev",
  "file": {
    "filename": "file_name.png",
    "contentType": "image/png",
    "path": "/tmp/1660312156425-file_name.png"
  },
  "message": "This is a test message!"
}

As you can see above, the response has the file saved in the “tmp” folder in the Lambda environment. You get back the path of the file so that you can process it in whatever way you like.

AWS Lamda tmp directory storage sizeAs you can see, the “tmp” directory can store files of quite a large size as per this official AWS doc.

Situation 3: Making a AJAX POST Request Via A Webpage To A API Gateway + AWS Lambda URL

I have create a JQuery AJAX POST request like so..

$.ajax({
    url: "https://lfdev2123.execute-api.ap-south-1.amazonaws.com/default/postParamsDemo",
    type: "POST",
    crossDomain: true,
    data: JSON.stringify({
        name: 'Livefire',
        domain: 'livefiredev.com'
    }),
    dataType: "json",
    success: function (response) {
        $("#result_json").val(JSON.stringify(response));
    },
    error: function (xhr, status) {
        alert("error");
    }
});

Here lets look at what the RAW event looks like..

Lets look at the RAW Event Data..

{
  "version": "1.0",
  "resource": "/postParamsDemo",
  "path": "/default/postParamsDemo",
  "httpMethod": "POST",
  "headers": {
    "Content-Length": "46",
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    "Host": "e5p88is0ub.execute-api.ap-south-1.amazonaws.com",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36",
    "X-Amzn-Trace-Id": "Root=1-62f5ad89-38e3d42b165c3be622e14b14",
    "X-Forwarded-For": "152.57.195.102",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https",
    "accept": "application/json, text/javascript, */*; q=0.01",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
    "origin": "http://127.0.0.1:5500",
    "referer": "http://127.0.0.1:5500/",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site",
    "sec-gpc": "1"
  },
  "multiValueHeaders": {
    "Content-Length": [
      "46"
    ],
    "Content-Type": [
      "application/x-www-form-urlencoded; charset=UTF-8"
    ],
    "Host": [
      "e5p88is0ub.execute-api.ap-south-1.amazonaws.com"
    ],
    "User-Agent": [
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36"
    ],
    "X-Amzn-Trace-Id": [
      "Root=1-62f5ad89-38e3d42b165c3be622e14b14"
    ],
    "X-Forwarded-For": [
      "152.57.195.102"
    ],
    "X-Forwarded-Port": [
      "443"
    ],
    "X-Forwarded-Proto": [
      "https"
    ],
    "accept": [
      "application/json, text/javascript, */*; q=0.01"
    ],
    "accept-encoding": [
      "gzip, deflate, br"
    ],
    "accept-language": [
      "en-GB,en-US;q=0.9,en;q=0.8"
    ],
    "origin": [
      "http://127.0.0.1:5500"
    ],
    "referer": [
      "http://127.0.0.1:5500/"
    ],
    "sec-fetch-dest": [
      "empty"
    ],
    "sec-fetch-mode": [
      "cors"
    ],
    "sec-fetch-site": [
      "cross-site"
    ],
    "sec-gpc": [
      "1"
    ]
  },
  "queryStringParameters": null,
  "multiValueQueryStringParameters": null,
  "requestContext": {
    "accountId": "383322943",
    "apiId": "e5p88is0ub",
    "domainName": "e5p88is0ub.execute-api.ap-south-1.amazonaws.com",
    "domainPrefix": "e5p88is0ub",
    "extendedRequestId": "WugNfiyghcwEJTA=",
    "httpMethod": "POST",
    "identity": {
      "accessKey": null,
      "accountId": null,
      "caller": null,
      "cognitoAmr": null,
      "cognitoAuthenticationProvider": null,
      "cognitoAuthenticationType": null,
      "cognitoIdentityId": null,
      "cognitoIdentityPoolId": null,
      "principalOrgId": null,
      "sourceIp": "152.57.195.102",
      "user": null,
      "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36",
      "userArn": null
    },
    "path": "/default/postParamsDemo",
    "protocol": "HTTP/1.1",
    "requestId": "WugNfiyghcwEJTA=",
    "requestTime": "12/Aug/2022:01:31:53 +0000",
    "requestTimeEpoch": 1660267913300,
    "resourceId": "ANY /postParamsDemo",
    "resourcePath": "/postParamsDemo",
    "stage": "default"
  },
  "pathParameters": null,
  "stageVariables": null,
  "body": "eyJuYW1lIjoiTGl2ZWZpcmUiLCJkb21haW4iOiJsaXZlZmlyZWRldi5jb20ifQ==",
  "isBase64Encoded": true
}

Again, its base64 encoded.

Parsing this data is simple. Just do something like..

let encoded = "eyJuYW1lIjoiTGl2ZWZpcmUiLCJkb21haW4iOiJsaXZlZmlyZWRldi5jb20ifQ==";

let getJSONFromRequest = (encodedData) => {

    let decoded = atob(encodedData);
    return JSON.parse(decoded);

}

console.log(getJSONFromRequest(encoded));

And you get back..

{ name: 'Livefire', domain: 'livefiredev.com' }

But wait. You might be getting a CORS errors in the browser when trying to make this request.

Bonus: How To Fix CORS Errors When Making AJAX Requests To AWS Lambda

If you try to run the above JQuery POST (Ajax) code in the browser, you will see that it fails.

In the browser console, you will see a CORS error.

CORS error when trying to access AWS Lamda via API gateway through an AJAX request

What Is CORS?

Below is the explanation of what CORS is from the MDN website..

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.

For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts.

And here is a great and short video on the subject..

How To Allow AJAX Requests?

For this, you would have to do a few settings via API Gateway.

  1. Go to API Gateway and click on your API
  2. As the arrows indicate below, click on CORS & then on “Configure”

CORS settings in api gateway to allow AJAX requests

Finally, set the settings as shown in the image blow..

Open CORS settings to allow AJAX requests through

These settings are very open. You can choose more controlled settings. Here is a guide to all the settings and what they mean.

Once you do this, the AJAX request should start to work.

Hope this guide was useful!