SERVERLESS ARCHITECTURE and AWS Lambda -DynamoDB -SES
By: Emre Ayhan
Tags: AWS, Serverless, AWS Lambda, Aws Ses, Aws Dynamodb

SERVERLESS ARCHITECTURE and AWS Lambda -DynamoDB -SES

Amazon Web Services

How to create a serverless RESTful API with Node.js and AWS

CRUD operations with Lambda and DynamoDB

How to send emails with Amazon SES? Emre Ayhan Dec 13, 2020·9 min read

Hello everyone, in this article, I will try to explain the serverless architecture and the crud operations with aws, also tell you how to send e-mails to our users using the simple email service (SES).

First of all, let’s make a short and simple definition of serverless.

Serverless or FaaS (Function As a Service) It ensures that the resources of your application are completely managed by the Cloud Provider, so this management is not in our hands and we can offer our applications to our users with less effort.

These are architectural structures where you only write your code and give the information about which events will be triggered, and you are charged according to the amount of your transactions as a payment method.

One of the best benefits is that it either completely eliminates or simplifies our problem, such as application scaling, deployment, operating system or programming language updates.

Now let’s talk about which cloud provider we will use our serverless architecture with. AWS is here to come to our help and we will use it and its services to perform crud operations and email sending.

Let’s start with CRUD, and let’s get Lambda and DynamoDB with us, let me talk about them very briefly.

LAMBDA

Lambda is a compute service that lets you run code without provisioning or managing servers.

Lambda runs your code only when needed and scales automatically, from a few requests per day to thousands per second. You pay only for the compute time that you consume (there is no charge when your code is not running and it’s the best part of it :) ) [1]

We can set our code to be triggered automatically from other AWS services or call it directly from the web or mobile app, which I’ll explain in a moment, as I will show you how we use it in conjunction with DynamoDb.

DYNAMODB

DynamoDB is a fully managed NoSQL database service that provides fast and predictable performance with seamless scalability.

With DynamoDB, you can create database tables that can store and retrieve any amount of data and serve any level of request traffic. You can scale up or scale down your tables’ throughput capacity without downtime or performance degradation. [1]

Now that we have learned the definitions, we can move on to our example. We will build a serverless structure from scratch and on top of it we will create our Lambda functions and our Table, inside there we will perform the crud operations that we all know.

AWS offers you a 1-year free usage. For this, you need to create a user in the Amazon Console at here

It asks you for a $1 debit card, but doesn’t charge this fee unless you use any paid service.

Then you need to create a user in Identity and Access Management (IAM) service, you can create it by following the Create new user option. You will find an Access Key ID and Secret Key ID of the user you created.

I will leave a youtube link with a video explanation of all the operations that I will explain below. After reading the article, you can develop your own application by following it from there.

First, we open our terminal and install the serverless cli with the

npm install -g serverless

command. We write

serverless config credentials —provider aws — key (your Access Key Id) --secret (your Secret Key Id) —profile serverlessUser

command, so we create a profile called serverlessUser and connect to AWS with our key IDs. Then let’s create our first serverless application.

serverless create -template aws-nodejs -path feCore

Let’s open our application in vs code (or whichever IDE you’re using), then I open the terminal and type npm init -y and. I’m creating my package.json file.

You can also modify and distribute your Lambda functions through the console, Aws gives you such a possibility, but it will disable this possibility if your files are too large.

For this, we need to add the serverless webpack and adjust our settings so that unnecessary files are hidden in the console and regain editing capability.

npm install —save serverless-webpack npm install —save webpack

Running these commands. Then I create my webpack.config.js file.

webpack.config.js

module.exports = { target: 'node', mode: 'none' }

I am adding these codes to this file.

In my serverless.yml file, I first add the profile: serverlessUser phrase to the provider section, we created this profile while setting up our serverless.

I add serverless-webpack as a plugin because I want the application to be bundled with webpack.

I set it to package: individually: true.

plugins: - serverless-webpack package: individually: true

Now let’s create our table in DynamoDB. For this, in our

serverless.yml

custom: tableName: fe-developers resources: Resources: MyFirstDynamoDbTable: Type: AWS::DynamoDB::Table Properties: TableName: ${self:custom.tableName} AttributeDefinitions: - AttributeName: ID AttributeType: N KeySchema: - AttributeName: ID KeyType: HASH BillingMode: PAY_PER_REQUEST

We are adding this code block. In addition, we allow data reading and writing operations from Dynamo in the provider part.

iamRoleStatements: - Effect: Allow Action: - dynamodb:* Resource: '*'

As soon as we say sls deploy, our table will be created,

We can select and view DynamoDB from the services section of the AWS console.

There, we can create a new item by opening our table and saying create new item and we can determine which fields and what type will be found in our table.

Now we have a table and an item in it. Let’s create a get function with our lambda and see the item.

I create a folder called Lambdas and there are 2 more folders called endpoints and common in it. I’m creating a file called API_Responses.js inside the Common folder.

API_Responses.js

const Responses = { _200(data = {}) { return { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Methods': '*', 'Access-Control-Allow-Origin': '*', }, statusCode: 200, body: JSON.stringify(data) } }, _400(data = {}) { return { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Methods': '*', 'Access-Control-Allow-Origin': '*', }, statusCode: 400, body: JSON.stringify(data) } } } module.exports = Responses;

I’m creating a method that returns 200 and 400 status codes in API_Responses.js file.

I create a file called getItem.js in my endpoints folder and open my terminal first. I’m typing npm install — save aws-sdk command.

We have to add sdk to our project to reach Amozon’s services.

Then, instead of giving the name of my DynamoDb table manually, I want to get it from environment and I add tableName to my provider in my serverless file.

environment: tableName: ${self:custom.tableName}

getItem.js

const Responses = require('../common/API_Responses') const AWS = require('aws-sdk') const documentClient = new AWS.DynamoDB.DocumentClient(); const TableName = process.env.tableName exports.handler = async event => { if(!event.pathParameters || !event.pathParameters.ID){ Responses._400({message: 'ID or parameters not found'}) }

let ID = event.pathParameters.ID

const params = { TableName, Key: { ID: parseInt(ID) } } const res = await documentClient.get(params).promise() return Responses._200(res) }

In my getItem file, I called my responses, table name and DynamoDB.DocumentClient from aws-sdk, with this friend we can do get put post and delete operations. I created my parameters and gave it to the get function.

I go back to my serverless file again and added below codes under functions

getItem: handler: lambdas/endpoints/getItem.handler events: - http: path: get-item/{ID} method: GET cors: true

So it creates an endpoint for me. This endpoint will return my item to me as soon as I paste it into my browser and press enter.

Congratulations we created our first Lambda function :)

Now let’s create a new item, for this I create the file createItem.js in the endpoints folder.

In the same way, I make the necessary imports. We will create a post request, this request will have a body, I parse this body with JSON.parse(event.body),put it in the newItem and set my parameters.

createItem.js

const newItem = JSON.parse(event.body) const params = { TableName, Item: { ID: newItem.ID, FirstName: newItem.FirstName, LastName: newItem.LastName, Department:newItem.Department, } } const res = await documentClient.put(params).promise()

I have to specify this function in my serverless.yml file. I’m adding my new function under functions.

createItem: handler: lambdas/endpoints/createItem.handler events: - http: path: create-item/{ID} method: POST cors: true

Since it will be a POST request, I enter POST as a method and specify the path.

Let me briefly explain our deletion and update requests.

deleteItem.js

exports.handler = async event => { if(!event.pathParameters || !event.pathParameters.ID){ Responses._400({message: 'ID or parameters not found'}) }

let ID = event.pathParameters.ID

const params = { TableName, Key: { ID: parseInt(ID) } } const res = await documentClient.delete(params).promise() return Responses._200(res) }

updateItem.js

exports.handler = async event => { if(!event.pathParameters || !event.pathParameters.ID){ Responses._400({message: 'ID or parameters not found'}) }

let ID = event.pathParameters.ID const updatedItem = JSON.parse(event.body) const params = { TableName, Key: { ID: parseInt(ID) }, UpdateExpression: "SET

FirstName=:fn,

LastName=:ln,

Department=:dp", ExpressionAttributeValues: { ':fn': updatedItem.FirstName, ':ln': updatedItem.LastName, ':dp': updatedItem.Department, }, ReturnValues:"UPDATED_NEW" } const res = await documentClient.update(params).promise() return Responses._200(res) }

In the update, except for our other parameters, we need to specify which fields in our table we will set and with which values we will update these fields.

In the UpdateExpression section, we specify our fields. In the ExpressionAttributeValues section, we map the required fields from within our updateItem object that we sent in the body.

Let’s add these functions in our serverless.yml

serverless.yml

deleteItem: handler: lambdas/endpoints/deleteItem.handler events: - http: path: delete-item/{ID} method: DELETE cors: true updateItem: handler: lambdas/endpoints/updateItem.handler events: - http: path: update-item/{ID} method: PUT cors: true

We use DELETE as a method to delete an item from the table. We use the PUT method to update it.

Now that we have finished our CRUD operations, we can move on to sending an email.

Let’s create a file called sendEmail in our Endpoints folder.

All our previous operations were performed with DynamoDB, and we will do this through SES.

sendEmail.js

const Responses = require('../common/API_Responses') const AWS = require('aws-sdk') const SES = new AWS.SES()

exports.handler = async event => { const {to,from,subject,text} = JSON.parse(event.body); const params = { Destination : { ToAddresses : [ to ] }, Message: { Body: { Text: { Data: text} }, Subject: { Data: subject} }, Source: from } try { await SES.sendEmail(params).promise() return Responses._200({}) } catch (error) { return Responses._400({message: 'failed to send email',error}) } }

We added SES over AWS.

I parce the body and set it to my required parameters. We have fields such as to, from, subject and text.

Let me give you some information about Simple Email Service.

SES

We can search the Console as SES and go to the page of the relevant service. On the page that opens, there is a field called email address on the left side, we have to verify the e-mail address we want to send, in that field we click on verify a new email address and enter the email address that we want to send mail.

After that, your e-mail address will appear in the list as verified. Amazon offers you a daily limit of up to 200 mails per day. If you have a necessary request, you can make a request to Amazon from Request Increased Sending Limits. If accepted, you reach the daily limit of 50k mail.

Going back to our code, we added SES, we are giving our parameters to the sendEmail method of SES. Finally, let’s add our sendEmail function to our serverless file.

sendEmail: handler: lambdas/endpoints/sendEmail.handler events: - http: path: send-email method: POST cors: true

And of course we have to allow our user to send mail, how we allowed dynamodb.

I also add SES to the iamRoleStatements field in my provider.

iamRoleStatements: - Effect: Allow Action: - dynamodb:* - ses:* Resource: '*'

Final view of our project

And our article ends here. I hope it was an educational article, as I mentioned at the beginning of my article, I made a youtube video where I did these procedures.

If you are someone who understand things better visually, like me, I would recommend watching the video at a speed of at least 1.5x, ideally 1.75x, and you will spend your precious minutes more efficiently.

Thank you for taking the time to read.

Youtube: here you can find the related video References

[1] : https://docs.aws.amazon.com -> here