React.js contact form with AWS Lambda and SES to send email! (Part - 2)

React.js contact form with AWS Lambda and SES to send email! (Part - 2)

Continuing our tutorial to create a full-stack React app with AWS Server-less back-end, lets jump to the terminal.

But before that, if you missed the first part here is the link

Step 1:
mdkir myApp
cd myApp
serverless create --template aws-nodejs --path api

Above command will create a API project for us by the name of api (any custom name).

I will be using VSCode as my code editor.

code .

After opening the project folder in VSCode and you can see there will be three files by the name of -

  • .gitignore
  • handler.js
  • serverless.yml

First, we will observe our serverless.yml, which will basically configure what our server-less package will do when we deploy it. We can see terms like service: api which tells what type of service we are gonna use, provider name:aws which tells us that aws is our service provider here, with runtime: nodejs12.x node as our runtime environment. Down the file we could see the per-defined function hello with handler: handler.hello given handler for our testing purposes. There are also many other features which are commented but we will leave that for now.

Looking into handler.js we can see the per-defined hello function has a handler exported in this file which returns some JSON.

Step 2:

We can test and deploy our hello function but we will rather jump to creating our email api here.

We will create a new file named sendEmail.js which will be the code for our lambda.

const AWS = require('aws-sdk');
const ses = new AWS.SES();

We first need to import our AWS and SES packages to actually work with them.

Step 3:
exports.handler = async event => {

    const { to, from, subject, text } = JSON.parse(event.body);

    if (!to || !from || !subject || !text) {
        return res_400({ message: 'missing parameter on request body' })
    }

Our handler will be a async event. We will first destructure the body which we will receive through our front-end.


NOTE

To keeps things simple and short we will be taking four values from the user in our react app, one can definitely customize these according to their needs

Step 4:

Now we will actually check if all the values are actually being passed to the API : -

  if (!to || !from || !subject || !text) {
        return res_400({ message: 'missing parameter on request body' })
    }

The above code will check for either of the missing parameter and send error message. The res_400 will be our custom function which will take object as an parameter.

Step 5:
const res_400 = body => {
    return {
        headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Methods': '*',
            'Access-Control-Allow-Origin': '*',
        },
        statusCode: 400,
        body: JSON.stringify(body)
    }
}

The res_400 function will accept body as a parameter and return as JSON string with also a status code of 400 whereas we will pass three headers to get our API working. The first header application/json tells us what kind of response is being returned, Methods second header tells us what kind of methods i.e. POST, GET, etc. kind of method is being used here (we can actually specify POST here, since we are using POST request here) and the last header is Origin which will help us in cors.

Step 6:

Now we will create the parameters tp pass to our AWS SES.

 const emailParam = {
        Destination: {
            ToAddresses: [to]
        },
        Message: {
            Body: {
                Text: { Data: text }
            },
            Subject: { Data: subject }
        },
        Source: from
    }

One can refer to the SES docs to understand the different parameters one can use. We will be using above parameters for our use case.

  • Destination: Object with array of two addresses with array containing one item, i.e. the email to whom we sending this email.
  • Message: It has a body, whereas in our case the body is a text and the text field has a data value of text.
  • Subject: It has a data field
  • Source: The email from whom we are sending the email from
Step 7:
    },
    Source: from
  }

try {
        await ses.sendEmail(emailParams).promise();

        return res_200()
    } catch (err) {
        console.log('Error in sending email!', err);

        return res_400({ message: 'Unable to send email...' })
    }
}

We will be using try - catch for better code execution. Since our SES function is an async function we have to use await while we will use promise to return our value.

We will be using another custom function res_200 which won't be using any parameters.

const res_200 = () => {
    return {
        statusCode: 200,
        headers: {
            "Access-Control-Allow-Headers": "Content-Type",
            "Access-Control-Allow-Origin": "*" // Allow from anywhere 
        },
        statusCode: 200,
        body: '',
    }
}

Our final sendEmail.js will look like : -

const AWS = require('aws-sdk');
const ses = new AWS.SES();

exports.handler = async event => {

    const { to, from, subject, text } = JSON.parse(event.body);

    if (!to || !from || !subject || !text) {
        return res_400({ message: 'missing parameter on request body' })
    }

    const emailParams = {
        Destination: {
            ToAddresses: [to]
        },
        Message: {
            Body: {
                Text: { Data: text }
            },
            Subject: { Data: subject }
        },
        Source: from
    }

    try {
        await ses.sendEmail(emailParams).promise();

        return res_200()
    } catch (err) {
        console.log('Error in sending email!', err);

        return res_400({ message: 'Unable to send email...' })
    }
}

const res_400 = body => {
    return {
        headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Methods': '*',
            'Access-Control-Allow-Origin': '*',
        },
        statusCode: 400,
        body: JSON.stringify(body)
    }
}


const res_200 = () => {
    return {
        statusCode: 200,
        headers: {
            "Access-Control-Allow-Headers": "Content-Type",
            "Access-Control-Allow-Origin": "*" // Allow from anywhere 
        },
        statusCode: 200,
        body: '',
    }
}
Step 8:

Coming to the final section of our API deployment, we need to configure our serverless.yml file to add our newly created function.

The final code will look like the above where one has to really take care of the indentation as I myself failed to deploy the first time due to bad indentation.

provider:
  name: aws
  runtime: nodejs12.x
  profile: testServerlessUser
  region: ap-south-1
  iamRoleStatements:
    - Effect: Allow
      Action:
        - ses:*
      Resource: '*'

Since we are using AWS SES we have to provide our lambda function some extra permission, by configuring our provider. We are basically allowing our IAM user to perform any SES actions with the available resources.

functions:
    hello:
        handler: handler.hello
        events:
            - http:
                  path: hello-test
                  method: GET
                  cors: true
    sendEmail:
        handler: sendEmail.handler
        events:
            - http:
                  path: send-email
                  method: post
                  cors: true

We will add the sendEmail function with the handler and specify events. The events will be almost similar to the above function just that the path and method of our http will differ. POST will be our method since we are gonna perform a POST request whereas our path will be send-email.

Step 9:
sls deploy

The above command will simply deploy our API and return us a endpoint to work with.

We also need to note down the POST endpoint returned as we will test that API in a minute whereas that's the API which will send our emails.

Step 10:

Before testing our API we first have to do a little configuration in AWS management console > SES.

Now before actually sending emails, we first need to verify the email through which we are gonna send the emails.

We need to click on Email Addresses on the left panel and then enter our email address. After clicking verify this email address we will receive a verification email from AWS. After verifying we can thus use this email for our purpose.

Step 11:

Now to actually test that our endpoint does work we will be using postman tool. (One can use any Rest API testing tool)

We will put our POST endpoint in the url and then add data in the body (raw).


NOTE

For testing purpose we keep the to and from email the same as the one we verified from AWS and later in the section I will explain the steps on how to use any other email.


After hitting send we will get a 200 response which tells us our API call was successful.

You can now check the inbox of the email account used as there will be an email with same subject and text as specified.

Now we know that our API works, so we will move forward to our React app and also in next tutorial I will explain the steps needed to get our SES out of sandbox (for testing purposes).

I know this was a very long section but the setup was probably the most rewarding task which required great caution as there shouldn't be any mistakes or one could get stuck in the process.

Feel free to share your doubts and suggestions below! image.png