Search code examples
google-apps-scriptcontent-typepayloadurlfetch

Google Apps Script UrlFetchApp GET to a node.js endpoint results in an empty body


I am using Google Apps Script to make a UrlFetchApp call to a node.js API endpoint. I own both the Google Apps Script code and node.js code & endpoint, so I can watch what is happening.

When I use Postman, it works. But when I use GAS UrlFetchApp, it doesn't work, as req.body in node is empty { }. I even looked at the code that Postman creates for JavaScript Fetch and try to nearly duplicate it, except for things I know GAS' UrlFetchApp needs. I've done quite a few UrlFetchApp calls with GAS to various external endpoints. So, I'm not new, but can't figure this one out.

Here is my Google Apps Script code:

    var url = 'https://xxxxxxxxxxx.azurewebsites.net/api/account';

    var data = {
      "email": address,
      "apiKey": 'xxxxxxxxxxxxxxxxxxx'
    }

    var payLoadInfo = JSON.stringify(data);

    var options = {
      "method": "GET",
      "headers": {'Content-Type': 'application/json'},
      // 'Content-Type': 'application/json',
      // "contentType": 'application/json',
      redirect: 'follow',
      "muteHttpExceptions": true,
      "body": payLoadInfo,
      // "payload": JSON.stringify(data)
      // payload: payLoadInfo
    };

    var response = UrlFetchApp.fetch(url, options);

The commented out parts of "options" are several different ways I've tried to get it to work. I know in the past that I've usually used "payload" instead of "body" like I am this time (Postman suggested it). When I use "payload", it fails completely, not even getting to my node code. I also tried putting the apiKey in the header.

Here is the node.js API endpoint code:

  router.get("/account", async (req, res) => {

  var apiKey = process.env.API_KEY;

  console.log('apiKey = ' + apiKey);
  console.log('req.body = ' + JSON.stringify(req.body, null, 2));
  console.log('req.body.apiKey = ' + req.body.apiKey);

  if (req.body.apiKey != apiKey) {

    console.log('apiKey is not equal');
    res.status(401).send({ error: "You are not authorized." });

  } else {

    process the request...

  }

When I use "payload" in "options" I get a 500, and the server code never executes. When I use "body" in "options", I see the server code execute, but the console.log('req.body = ' + JSON.stringify(req.body, null, 2)), just comes back with an empty object {}, and then since req.body.apiKey != apiKey, it consoles "apiKey is not equal" and sends a 401. When using Postman, the req.body object consoles fine, showing the email & apiKey.

No matter what combinations of things I put into options, it fails either with 500 or 401. However, Postman works great with pretty much the same parameters, headers, etc.

Here is what Postman shows for the code:

var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Cookie", "ARRAffinity=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ARRAffinitySameSite=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

var raw = JSON.stringify({"email":"[email protected]","apiKey":"xxxxxxxxxxxxxxxx"});

var requestOptions = {
  method: 'GET',
  headers: myHeaders,
  body: raw,
  redirect: 'follow'
};

I even tried including the cookie and "redirect: follow", but none works.

What am I missing?


Solution

  • I got it to work, thanks to help from @Tanaike (see comments above).

    It seems that unlike normal "fetch" in node.js, URLFetchApp in Google Apps Script will not send a body along with a GET.

    I still used GET, but changed to sending the param in the URL and the apiKey in the header.