Serverless Application on AWS

Serverless Application on AWS

Serverless computing is a great way for application development. It eliminates the need to manage servers. The cloud providers even take care of scalability, monitoring, logging etc. This means as a developer you can completely focus on app development without worrying about the infrastructure and performance. And you will only be paying for the net usage of resources.

Let’s try to create a serverless application using AWS lambda, Dynamodb and API Gateway.

Components of application

Let’s try to create a serverless application using AWS lambda, Dynamodb and API Gateway.

  • API Gateway: API gateway is the entrypoint of our serverless application. It will handle the incoming requests from the clients and gave the response from the backend service. We will use API Gateways to create Rest APIs. API Gateway can handle the task like authentication, authorization and traffic management and monitoring.
  • Lambda funtion: Lambda function is small self contained units of code that run in response of events. They can be written in multiple languages like Python, Nodejs, Java and more. When an API Gateway receives a request, it can be configured to trigger a specific Lambda function. This function then processes the request, performs the necessary logic, and returns a response. The best thing about Lambda is that you only pay for the compute time used, making it highly cost-effective.
  • DynamoDB: DynamoDB is a fully managed NoSQL database service that offers fast and predictable performance with seamless scaling. We’ll use DynamoDB to store the data that our API will manage. We’ll create tables, define data schemas, and perform CRUD (Create, Read, Update, Delete) operations using DynamoDB’s powerful API.

Lab

Step 1: Create IAM role

  1. Go to IAM from your AWS console.
  2. Choose the “Roles” option from the left sidebar.
  3. Create a role with the following properties.
    • Trusted entity - Lambda
    • Role name - lambda-apigateway-role
    • Permissions - To allow lambda to read and write dynamodb and to write the cloud watch logs.
{
	"Version": "2012-10-17",
	"Statement": [
    {
      "Sid": "Statement1",
      "Action": [
        "dynamodb:DeleteItem",
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:UpdateItem"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Sid": "Statement2",
      "Resource": "*",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Effect": "Allow"
    }
	]
}

Step 2: Create Lambda function

  1. Click Create function in AWS Lambda Console
  2. Select Author from scratch. Use name MyLambdaFunction , select Python 3.12 as Runtime. Under Permissions, select Use an existing role, and select lambda-apigateway-role that we created, from the drop down.
  3. Click Create function
  4. Replace the boilerplate coding with the following code snippet. Click on Deploy.
from __future__ import print_function

import boto3
import json

print('Loading function')


def lambda_handler(event, context):
    '''Provide an event that contains the following keys:

      - operation: one of the operations in the operations dict below
      - tableName: required for operations that interact with DynamoDB
      - payload: a parameter to pass to the operation being performed
    '''
    #print("Received event: " + json.dumps(event, indent=2))

    operation = event['operation']

    if 'tableName' in event:
        dynamo = boto3.resource('dynamodb').Table(event['tableName'])

    operations = {
        'create': lambda x: dynamo.put_item(**x),
        'read': lambda x: dynamo.get_item(**x),
        'update': lambda x: dynamo.update_item(**x),
        'delete': lambda x: dynamo.delete_item(**x),
        'list': lambda x: dynamo.scan(**x),
        'echo': lambda x: x,
        'ping': lambda x: 'pong'
    }

    if operation in operations:
        return operations[operation](event.get('payload'))
    else:
        raise ValueError('Unrecognized operation "{}"'.format(operation))
  1. To test the function you can create a new Test event with this payload.
{
    "operation": "echo",
    "payload": {
        "somekey1": "somevalue1",
        "somekey2": "somevalue2"
    }
}

Click on “Test”.

Sample Output:

{
  "somekey1": "somevalue1",
  "somekey2": "somevalue2"
}

Step 3: Create DynamoDB

  1. Open the DynamoDB console.
  2. Choose Create table.
  3. Create a table with the following settings.
    • Table name – lambda-apigateway
    • Primary key – id (string)
  4. Choose Create.

Step 4: Create API Gateway

  1. Go to API Gateway console
  2. Scroll down and select Build for REST API
  3. Give the API name as Dynamo, keep everything as is, click Create API
  4. By default, you will get the root path. But we will create one endpoint path.
  5. Input dynamo in the Resource Name, Resource Path will get populated. Click `Create Resource”
  6. Let’s create a POST Method for our API. With the /dynamo resource selected, Click Actions again and click Create Method.
  7. Select POST from drop down , then click checkmark
  8. Select Lambda Function option for integration. Choose the lambda function that we have created earlier. Click Save.

    You should get the request-response workflow in the API Gateway page.

  9. Deploy the API
    1. Click on Deploy API button.
    2. Select New Stage
    3. Give prod as Stage Name. Now to invoke the API, go to the “Stages” from the left sidebar and get the endpoint for /dynamo path.

Step 5: Test the application

  • In Postman, you can use the URL from the API gateway and this payload to create some data in the dynamo db.
{
    "operation": "create",
    "tableName": "lambda-apigateway",
    "payload": {
        "Item": {
            "id": "1",
            "number": 12
        }
    }
}
  • To get the stored items list, you can use this payload to get the list of items stored in the database.
{
    "operation": "list",
    "tableName": "lambda-apigateway",
    "payload": {}
}

If these URLS gave you successfull response, then it means you application is working properly.

Congratulations on creating a serverless application from scratch. Now it’s your opportunity to modify the configuration and lambda function and expand your knowledge with serverless applications.

Feel free to watch our video for more detailed information. If you find the content useful, please subscribe to the channel.

Bonus

Here is the cloud formation template that can perform all the above steps at once.

AWSTemplateFormatVersion: '2010-09-09'
Description: Serverless application

Resources:
  # Create IAM Role for Lambda
  LambdaAPIGatewayRole:
    Type: 'AWS::IAM::Role'
    Properties: 
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement: 
          - Effect: 'Allow'
            Principal: 
              Service: 
                - 'lambda.amazonaws.com'
            Action: 
              - 'sts:AssumeRole'
      Policies:
        - PolicyName: 'LambdaDynamoDBPolicy'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action: 
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: 'arn:aws:logs:*:*:*'
              - Effect: 'Allow'
                Action: 
                  - 'dynamodb:*'
                  - 'dynamodb:DeleteItem'
                  - 'dynamodb:GetItem'
                  - 'dynamodb:PutItem'
                  - 'dynamodb:Query'
                  - 'dynamodb:Scan'
                  - 'dynamodb:UpdateItem'
                Resource: '*'
                
  # Create DynamoDB Table
  DynamoDBTable:
    Type: 'AWS::DynamoDB::Table'
    Properties:
      TableName: 'lambda-apigateway'
      AttributeDefinitions:
        - AttributeName: 'id'
          AttributeType: 'S'
      KeySchema:
        - AttributeName: 'id'
          KeyType: 'HASH'
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5

  # Create Lambda Function
  MyLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties: 
      FunctionName: 'MyLambdaFunction'
      Handler: 'index.lambda_handler'
      Runtime: 'python3.12'
      Role: !GetAtt LambdaAPIGatewayRole.Arn
      Code:
        ZipFile: |
          from __future__ import print_function

          import boto3
          import json

          print('Loading function')


          def lambda_handler(event, context):
              '''Provide an event that contains the following keys:

                - operation: one of the operations in the operations dict below
                - tableName: required for operations that interact with DynamoDB
                - payload: a parameter to pass to the operation being performed
              '''
              #print("Received event: " + json.dumps(event, indent=2))

              operation = event['operation']

              if 'tableName' in event:
                  dynamo = boto3.resource('dynamodb').Table(event['tableName'])

              operations = {
                  'create': lambda x: dynamo.put_item(**x),
                  'read': lambda x: dynamo.get_item(**x),
                  'update': lambda x: dynamo.update_item(**x),
                  'delete': lambda x: dynamo.delete_item(**x),
                  'list': lambda x: dynamo.scan(**x),
                  'echo': lambda x: x,
                  'ping': lambda x: 'pong'
              }

              if operation in operations:
                  return operations[operation](event.get('payload'))
              else:
                  raise ValueError('Unrecognized operation "{}"'.format(operation))

  # Create API Gateway
  MyApiGateway:
    Type: 'AWS::ApiGateway::RestApi'
    Properties:
      Name: 'DynamoDBOperations'

  # Create API Gateway Resource
  ApiResource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
      RestApiId: !Ref MyApiGateway
      ParentId: !GetAtt MyApiGateway.RootResourceId
      PathPart: 'dynamodbmanager'

  # Create API Gateway Method
  ApiMethod:
    Type: 'AWS::ApiGateway::Method'
    Properties:
      MethodResponses: 
        - ResponseModels: 
            application/json: Empty
          StatusCode: 200
      ResourceId: !Ref ApiResource
      RestApiId: !Ref MyApiGateway
      HttpMethod: 'POST'
      AuthorizationType: 'NONE'
      Integration:
        Type: 'AWS'
        IntegrationHttpMethod: 'POST'
        Uri: 
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}/invocations
        IntegrationResponses: 
          - StatusCode: 200


  # Deployment of API Gateway
  ApiDeployment:
    Type: 'AWS::ApiGateway::Deployment'
    DependsOn: ApiMethod
    Properties:
      RestApiId: !Ref MyApiGateway
      StageName: 'prod'

  # Permission for API Gateway to invoke Lambda
  LambdaApiGatewayInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties: 
      FunctionName: !GetAtt MyLambdaFunction.Arn
      Action: 'lambda:InvokeFunction'
      Principal: 'apigateway.amazonaws.com'
      SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${MyApiGateway}/*/*/dynamodbmanager'

Outputs:
  ApiUrl:
    Description: "URL for the API endpoint"
    Value: 
      Fn::Sub: 
        - https://${MyApiGateway}.execute-api.${AWS::Region}.amazonaws.com/prod/dynamodbmanager
        - MyApiGateway: !Ref MyApiGateway

Conclusion:

By leveraging serverless architecture, we eliminated the complexities of managing infrastructure, allowing us to focus solely on application logic. The pay-per-use model of AWS Lambda ensures cost-efficiency, making it an ideal choice for various applications.

Through hands-on experience, we gained insights into the power of combining these services to create a seamless and responsive application. Whether you’re a seasoned developer or just starting, understanding serverless computing and these AWS services is essential for building modern, cloud-native applications.

We encourage you to experiment further with AWS Lambda, DynamoDB, and API Gateway to unlock the full potential of serverless computing.