How does one POST an AWS S3 hosted contact form using javascript to an AWS api gateway endpoint for processing on a nodejs labmbda function

342 views Asked by At

In advance, thank you for reading this long question. To start with I have this static AWS S3 hosted HTML form that I'd like to use to gather a requester's contact information/question so I can contact them for whatever question they might pose. The HTML for that form looks like this:

    <form id="contact-form">
       <div class="form-group">
          <label>First and Last Name</label>
          <input id="name-input" name="name" type="text" class="form-control" required="" placeholder="Enter your name here.....">
       </div>
       <div class="row">
          <div class="col-md-6">
             <div class="form-group">
                <label>Email</label>
                <input id="email-input" name="email" type="email" class="form-control" required="" placeholder="Enter your mail address here.....">
             </div>
          </div>
          <div class="col-md-6">
             <div class="form-group">
                <label>Phone</label>
                <input id="phone-input" name="phone" type="phone" class="form-control" required="" placeholder="Enter your phone number">
             </div>
          </div>
         </div>
       <div class="form-group">
         <label>Subject</label>
         <input name="subject" type="text" class="form-control" required="" id="subject" placeholder="Subject">
       </div>
       <div class="form-group">
          <label>Message</label>
          <textarea id="message" name="message" class="form-control" required="" placeholder="Write you message here..." style="height:100px;"></textarea>
       </div>
       <input type="submit"/>
    </form>

As you can tell there is absolutely nothing interesting about this form. This form gets processed by some JavaScript that looks like this:

    <script async type="text/javascript">
                const form = document.querySelector("form");
    form.addEventListener("submit", (event) => {
      // prevent the form submit from refreshing the page
      event.preventDefault();
    
      const { name, email, phone, subject, message } = event.target;
    
        // Use your API endpoint URL you copied from the previous step
      const endpoint = "<https://MyApiGatewayEndpoint-east-1.amazonaws.com/test/mailfwd>";
      // We use JSON.stringify here so the data can be sent as a string via HTTP
        const body = JSON.stringify({
        senderName: name.value,
        email: email.value,
        phone: phone.value,
        subject: subject.value,
        message: message.value
      });
      
      
      window.alert("Body is " + body);
      const requestOptions = {
        method: "POST",
        body
      };
    
      fetch(endpoint, requestOptions)
        .then((response) => {
          if (!response.ok) throw new Error("Error in fetch");
          return response.json();
        })
        .then((response) => {
          document.getElementById("result-text").innerText =
            "Email sent successfully!";
        })
        .catch((error) => {
          document.getElementById("result-text").innerText =
            "An unkown error occured.";
        });
    });
    </script>

As you can tell this too is in no way interesting. This JavaScript simply grabs the form submit and is supposed to send a POST transaction to my AWS API Gateway endpoint that then passes it along to my NodeJS lambda function and sends me an email using AWS SES. For reference here is what my NodeJS Lambda function looks like:

    const aws = require("aws-sdk");
    const ses = new aws.SES({ region: "us-east-1" });
    exports.handler = async function (event) {
      console.log('EVENT: ', event)
        // Extract the properties from the event body
      const { senderName, senderEmail, senderPhone, subject, message } = JSON.parse(event.body)
      const params = {
        Destination: {
          ToAddresses: ["[email protected]"],
        },
            // Interpolate the data in the strings to send
        Message: {
          Body: {
            Text: { 
                Data: `You just got a message from ${senderName} at phone number ${senderPhone} - ${senderEmail}:
                ${message}` 
            },
          },
          Subject: { Data: `Message regarding ${subject}` },
        },
        Source: "[email protected]",
      };
    
      return ses.sendEmail(params).promise();
    };

The problem is when I debug the page during a form submit button click in the Chrome developer tools I get an error that says:

contact.html:459 POST https://s3.us-east-2.amazonaws.com/n.com/%3Chttps://.amazonaws.com/test/mailfwd%3E 405

This confuses me because it looks like the post from the JavaScript is first being sent to my S3 bucket with instructions for the S3 bucket to forward the POST along to my API Gateway EndPoint. I know sending a POST to an S3 bucket from a form submission won't fly Soooooo ....... how do I get my JavaScript to send this post directly to my API Gateway EndPoint. Incidentally, I can send a post directly to this API Gateway EndPoint using curl with the values my NodeJS Lambda function expects and everything works as expected and my "Contact Us" email arrives at my recipient address. This means I know the API Gateway EndPoint will gladly accept a direct post and work happily with my AWS Lambda function. Thanks in advance for any suggestions or answers anyone provides.

-J

Please see my problem description above.

1

There are 1 answers

1
IMSoP On

The most telling part of the error message you posted is that it contains %3C and %3E - some characters that have been escaped because they don't normally appear in URLs.

Checking an ASCII table confirms that they are < and > and looking at your code I see this:

const endpoint = "<https://MyApiGatewayEndpoint-east-1.amazonaws.com/test/mailfwd>";

That's not a well-formed URL, so the browser is trying to guess what you mean by it. It's deciding that it's a relative URL, and generating a full URL by combining it with the URL of the current page, as it would if you just specified a file name like "process.php".

What you actually wanted was an absolute URL, which just looks like this:

const endpoint = "https://MyApiGatewayEndpoint-east-1.amazonaws.com/test/mailfwd";

Possibly you looked at some sample code with something like <your URL here> and misunderstood.