1

I am trying to create an application in my local system and deploy it to the AWS cloud using SAM CLI. The basic outline of this application is given in the diagram.

enter image description here

I have created a directory named myproj for this application and all the sub-folders and files are shown in the following diagram.

enter image description here

The template.yaml file consists of the following code -

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  myDB:
    Type: AWS::Serverless::SimpleTable
    Properties:
      TableName: tabmine
      PrimaryKey:
        Name: id
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5
        
  LambdaWrite:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: functionWrite/
      Handler: write.handler
      Runtime: nodejs12.x
      Events:
        apiForLambda:
          Type: Api 
          Properties:
            Path: /writedb
            Method: post
      Policies:
        DynamoDBWritePolicy:
          TableName: !Ref myDB
            
  LambdaRead:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: functionRead/
      Handler: read.handler
      Runtime: nodejs12.x
      Events:
        apiForLambda:
          Type: Api 
          Properties:
            Path: /readdb
            Method: post
      Policies:
        DynamoDBReadPolicy:
          TableName: !Ref myDB

In the functionRead folder, package.json has the following contents -

{
  "name": "myproj",
  "version": "1.0.0",
  "description": "",
  "main": "read.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "aws-sdk": "^2.783.0"
  }
}

And the read.js file contains the following code -

var AWS = require('aws-sdk');

var ddb = new AWS.DynamoDB.DocumentClient();

exports.handler = function(event, context, callback) {
    var ID = event.id;
    
    var params = {
        TableName: 'tabmine',
        Key: {
            'id': ID
        }
    };
    
    ddb.get(params, callback);
    
};

In functionWrite folder, the file package.json has the following content -

{
  "name": "myproj",
  "version": "1.0.0",
  "description": "",
  "main": "write.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "aws-sdk": "^2.783.0"
  }
}

And the file write.js has the following content -

var AWS = require('aws-sdk');

var ddb = new AWS.DynamoDB.DocumentClient();

exports.handler = function(event, context, callback) {
    

    var ID = event.id;
    var NAME = event.name;
    
    var params = {
        TableName: 'tabmine',
        Item: {
            'id': ID,
            'name': NAME
        }
    };
    
    ddb.put(params, callback);
    
};

Then, I have navigated back to the myproj directory in the terminal and ran the command sam build. After building was done, I ran the command sam deploy --guided and followed the steps to deploy the stack to the cloud. Then, I checked the console to confirm the deployment and it was successful. Then, in the terminal, I ran curl -X POST -d '{"id":"one","name":"john"}' https://0000000000.execute-api.ap-south-1.amazonaws.com/Prod/writedb. But I got {message : 'Internal server Error'}.

To confirm if the lambda and dynamoDB are correctly linked or not, I went to Lambda console and created a test event for the lambda function with name write.js with the same payload {"id":"one","name":"john"}.It ran successfully and entered the those two items into the dyanmoDB table. Similarly, I have created another test event for the lambda function with name read.js and with payload {"id":"one"}. It also ran successfully and displayed the datas.

To confirm if the API gateway and the lambdas are are correctly linked or not, I ran the test in API gateway for both the resources /writedb and /readdb, but it is giving me {message : 'Internal server Error'}.

Please help me out of this problem.

Souvik paul
  • 403
  • 1
  • 6
  • 10
  • I do not see the part where you are declaring API Gateway in your template.yaml AWS::Serverless::Api can you include that? Otherwise how are you getting the endpoint and stage? – David Webster Nov 01 '20 at 21:38
  • According to the AWS documentation - An AWS::Serverless::Api resource need not be explicitly added to a AWS Serverless Application Definition template. A resource of this type is implicitly created from the union of Api events defined on AWS::Serverless::Function resources defined in the template that do not refer to an AWS::Serverless::Api resource. See - https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html . All though I found the solution to this problem, which I am going to write in the Answer. Thanks for your concern. – Souvik paul Nov 02 '20 at 20:01

1 Answers1

2

The event object that will come from the API gateway invocation will have the following structure -

{
  resource: '/func1',
  path: '/func1',
  httpMethod: 'POST',
  headers: {
    accept: '*/*',
    'content-type': 'application/x-www-form-urlencoded',
    Host: '0000000000.execute-api.ap-south-1.amazonaws.com',
    'User-Agent': 'curl/7.71.1',
    'X-Amzn-Trace-Id': 'Root=1-5fa018aa-60kdfkjsddd5f6c6c07a2',
    'X-Forwarded-For': '178.287.187.178',
    'X-Forwarded-Port': '443',
    'X-Forwarded-Proto': 'https'
  },
  multiValueHeaders: {
    accept: [ '*/*' ],
    'content-type': [ 'application/x-www-form-urlencoded' ],
    Host: [ '0000000000.execute-api.ap-south-1.amazonaws.com' ],
    'User-Agent': [ 'curl/7.71.1' ],
    'X-Amzn-Trace-Id': [ 'Root=1-5fa018aa-603d90fhgdhdgdjhj6c6dfda2' ],
    'X-Forwarded-For': [ '178.287.187.178' ],
    'X-Forwarded-Port': [ '443' ],
    'X-Forwarded-Proto': [ 'https' ]
  },
  queryStringParameters: null,
  multiValueQueryStringParameters: null,
  pathParameters: null,
  stageVariables: null,
  requestContext: {
    resourceId: 'scsu6k',
    resourcePath: '/func1',
    httpMethod: 'POST',
    extendedRequestId: 'VYjfdkjkjfjkfuA=',
    requestTime: '02/Nov/2020:14:33:14 +0000',
    path: '/test/func1',
    accountId: '00000000000',
    protocol: 'HTTP/1.1',
    stage: 'test',
    domainPrefix: 'f8h785q05a',
    requestTimeEpoch: 1604327594697,
    requestId: '459e0256-9c6f-4a24-bcf2-05520d6bc58a',
    identity: {
      cognitoIdentityPoolId: null,
      accountId: null,
      cognitoIdentityId: null,
      caller: null,
      sourceIp: '178.287.187.178',
      principalOrgId: null,
      accessKey: null,
      cognitoAuthenticationType: null,
      cognitoAuthenticationProvider: null,
      userArn: null,
      userAgent: 'curl/7.71.1',
      user: null
    },
    domainName: '000000000.execute-api.ap-south-1.amazonaws.com',
    apiId: 'lkjfslkfj'
  },
  body: '{"id":"1","name":"John"}',
  isBase64Encoded: false
}

From the above event which is JSON object, we need the body. Since, the body is a string, so we need to convert it into JSON object.

So, write.js should be modified to -

var AWS = require('aws-sdk');
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});

exports.handler = async (event) => {
    try {
        
            //console.log(event);
            //console.log(event.body);
            var obj = JSON.parse(event.body);
        
            //console.log(obj.id);
            //console.log(obj.name);
        
            var ID = obj.id;
            var NAME = obj.name;
        
        
            var params = {
                TableName:'tabmine',
                Item: {
                 id : {S: ID}, 
                 name : {S: NAME}
                }
            
            };
        
            var data;
            var msg;
            
            try{
                data = await ddb.putItem(params).promise();
                console.log("Item entered successfully:", data);
                msg = 'Item entered successfully';
            } catch(err){
                console.log("Error: ", err);
                msg = err;
            }
        
        
            var response = {
                'statusCode': 200,
                'body': JSON.stringify({
                    message: msg
            })
        };
    } catch (err) {
        console.log(err);
        return err;
    }

    return response;
};

Similarly, read.js will get modified to -

var AWS = require('aws-sdk');
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});

exports.handler = async (event) => {
    try {
        
            //console.log(event);
            //console.log(event.body);
            var obj = JSON.parse(event.body);
        
            //console.log(obj.id);
            //console.log(obj.name);
        
            var ID = obj.id;
        
        
            var params = {
                TableName:'tabmine',
                Key: {
                 id : {S: ID}
                }
            
            };
        
            var data;
            
            try{
                data = await ddb.getItem(params).promise();
                console.log("Item read successfully:", data);
            } catch(err){
                console.log("Error: ", err);
                data = err;
            }
        
        
            var response = {
                'statusCode': 200,
                'body': JSON.stringify({
                    message: data
            })
        };
    } catch (err) {
        console.log(err);
        return err;
    }

    return response;
};

So, this will solve the problem. And template.yaml file is also correct.

Souvik paul
  • 403
  • 1
  • 6
  • 10