Search code examples
node.jsemailgmail-api

GMail API using POST request, error code: 400, recipient address required


I am trying to send an email using POST request with just Node standard modules in NodeJS v8.10 via GMail API. The sample code is given below.

I am getting an error:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "invalidArgument",
    "message": "Recipient address required"
   }
  ],
  "code": 400,
  "message": "Recipient address required"
 }
}

It says recipient address required but according to what I think, (I may be wrong), my base64url conversion is proper since I checked it in Google API Explorer but my problem is in passing the data, I am doing it properly as told in the guide i.e. inside body with 'raw' key but it still does not work and this is where my problem must be. Maybe I am missing something, maybe I do not know the proper structure.

Yes, there are multiple posts regarding this but none of them provided solution. I referred the guide on https://developers.google.com/gmail/api/v1/reference/users/messages/send but the given example is of with the use of client library.

I tried everything, passing the 'raw' with base64url encoded data into write function of the request, passing it as a data parameter in options, passing it through body parameters in options, everything I can think of.

Am I missing something? Where am I going wrong?

I am a newbie in nodejs so please explain and if possible, an example structure of solution would be most welcome.

Base64url produced is working fine, I guess. I copied the string produced by conversion and tried it at https://developers.google.com/gmail/api/v1/reference/users/messages/send?apix=true It works fine and sends me the mail but it does not work on my code.

var email = (
          "Content-Type: text/plain; charset=\"UTF-8\"\n" +
          "Content-length: 5000\n" +
          "MIME-Version: 1.0\n" +
          "Content-Transfer-Encoding: message/rfc2822\n" +
          "to: [email protected]\n" +
          "from: \"Some Name\" <[email protected]>\n" +
          "subject: Hello world\n\n" +

          "The actual message text goes here"
            );

async function sendMail(token,resp) {

    return new Promise((resolve,reject) => {

      var base64EncodedEmail = Buffer.from(email).toString('base64'); 
      var base64urlEncodedEmail = base64EncodedEmail.replace(/\+/g, '-').replace(/\//g, '_');

      var params = {
        userId: 'me',
        resource: {
          'raw': base64urlEncodedEmail
        }
      };

      var body2 = {
          "raw": base64urlEncodedEmail,
        }

      var options = {
        hostname: 'www.googleapis.com',
        path:'/upload/gmail/v1/users/me/messages/send',
        headers: {
          'Authorization':'Bearer '+token,
          'Content-Type':'message/rfc822',
        },
        body: {
          "raw": base64urlEncodedEmail,
          'resource': {
            'raw': base64urlEncodedEmail,
          }
        },
        data: JSON.stringify({
          'raw': base64urlEncodedEmail,
          'resource': {
            'raw': base64urlEncodedEmail,
          }
        }),
        message: {
          'raw': base64urlEncodedEmail,
        },
        payload: {
          "raw": base64urlEncodedEmail, //this is me trying everything I can think of
        },
        // body: raw,
        // }
        userId: 'me',
        // resource: {
        //   'raw': base64urlEncodedEmail
        // },
        method: 'POST',
      };

    var id='';
    console.log(base64urlEncodedEmail);
    const req = https.request(options, (res) => {

        var body = '';

        res.on('data', (d) => {
            body += d;
        });
        res.on('end', () => {
            var parsed = body;
            console.log(parsed);
        })
      });

      req.on('error', (e) => {
        console.error(e);
      });
      req.write(JSON.stringify(body2));
      req.end();
  });
};

Thank you for your time and answers.


Solution

  • I found the solution.

    It says everywhere to convert the rfc822 formatted string to Base64url to send and attach it to 'raw' property in the POST body but I don't know what has changed and you don't need to do that anymore.

    First things first, the Content-Type in header should be

    'Content-Type':'message/rfc822'
    

    Now, since we are specifying the content-type as message/rfc822, we don't need to convert the data we want to send into base64url format anymore, I guess (Not sure of the reason because I have a very little knowledge about this.) Only passing "To: [email protected]" as body works.

    Here is the complete code of how to get it done for someone who is struggling for the same problem.

    function makeBody(to, from, subject, message) {
        let str = [
            "to: ", to, "\n",
            "from: ", from, "\n",
            "subject: ", subject, "\n\n",
            message,
        ].join('');
        return str;
    }
    
    
    async function getIdAsync(token,resp) {
    
        return new Promise((resolve,reject) => {
    
          let raw = makeBody("[email protected]", "[email protected]", "Subject Here", "blah blah blah");
    
          var options = {
            hostname: 'www.googleapis.com',
            path:'/upload/gmail/v1/users/me/messages/send',
            headers: {
              'Authorization':'Bearer '+token,
              'Content-Type':'message/rfc822'
            },
            method: 'POST',
          };
    
        const req = https.request(options, (res) => {
    
            var body = '';
    
            res.on('data', (d) => {
                body += d;
            });
            res.on('end', () => {
                var parsed = body;
                console.log(parsed);
            })
          });
    
          req.on('error', (e) => {
            console.error(e);
          });
          req.write(raw);
          req.end();
      });
    };
    
    

    Happy Coding :)