amazon-web-servicesaws-lambdaamazon-dynamodbamazon-cloudfrontserverless-framework

Unable to send GET request with AWS Lambda & DynamoDB Rest API using serverless


I am creating an API to make GET and POST request to a table in DynamoDB.

I deployed it using serverless and received the endpoints for each API type.

But when testing it out with Postman I get the following error:

Bad request. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner. 
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation. 

Code for creating the data in the table:


const postsTable = process.env.POSTS_TABLE;
// Create a response
function response(statusCode, message) {
    return {
        statusCode: statusCode,
        body: JSON.stringify(message)
    };
}

// Create a post
module.exports.createPost = (event, context, callback) => {
    const reqBody = JSON.parse(event.body);

    if (
        !reqBody.title ||
        reqBody.title.trim() === "" ||
        !reqBody.body ||
        reqBody.body.trim() === ""
    ) {
        return callback(
            null,
            response(400, {
                error:
                    "Post must have a title and body and they must not be empty"
            })
        );
    }

    const post = {
        id: uuidv4(),
        createdAt: new Date().toISOString(),
        userId: 1,
        title: reqBody.title,
        body: reqBody.body
    };

    return db
        .put({
            TableName: postsTable,
            Item: post
        })
        .promise()
        .then(() => {
            callback(null, response(201, post));
        })
        .catch(err => response(null, response(err.statusCode, err)));
};


Solution

  • I managed to do it but did not use Serverless.

    I set up Lambda functions to POST and GET the data from a url.

    I think the issue previously was to do with the policies. This time when making the Lambda functions I set it as the following:

    I clicked on "Create a new role from AWS policy templates" while creating an execution role for a new function, then selected "Simple microservice permissions" for Policy templates. This added Basic execution role policy and below DynamoDB permissions to the role for all the tables in the same region as the function :

    "Action": [
                   "dynamodb:DeleteItem",
                   "dynamodb:GetItem",
                   "dynamodb:PutItem",
                   "dynamodb:Scan",
                   "dynamodb:UpdateItem"
               ]
    

    Lambda function for POST request

    exports.handler = async (event, context) => {
        const ddb = new AWS.DynamoDB({ apiVersion: "2012-10-08" });
        const documentClient = new AWS.DynamoDB.DocumentClient({
            region: "ap-southeast-1"
        });
    
        let responseBody = "";
        let statusCode = 0;
    
        const {
            deviceId,
            batteryLevel,
            eventId,
            id,
            location,
            tags,
            time
        } = JSON.parse(event.body);
    
        const params = {
            TableName: "dashboard",
            Item: {
                batteryLevel: batteryLevel,
                deviceId: deviceId,
                eventId: eventId,
                location: location,
                tags: tags,
                time: time
            }
        };
    
        try {
            const data = await documentClient.put(params).promise();
            responseBody = JSON.stringify(data);
            statusCode = 201;
        } catch (err) {
            responseBody = "Unable to POST data";
            statusCode = 403;
        }
        const response = {
            statusCode: statusCode,
            headers: {
                myHeader: "test"
            },
            body: responseBody
        };
        return response;
    };
    

    Other issues as well were with the method execution of the API I needed to set a custom model for the Request Body to match my data:

    {
      "$schema": "http://json-schema.org/draft-04/schema#",
      "title": "DashboardInputModel",
      "type": "object",
      "properties": 
      {
      "batteryLevel": {"type": "string"},
      "deviceId": {"type": "string"},
      "eventId": {"type": "string"},
      "id": {"type": "number"},
      "location": {
          "type": "object",
          "properties":{
                "accuracy": {"type": "number"},
                "latitude": {"type": "number"},
                "longitude": {"type": "number"}
          }
      },
      "tags": {
       "type": "array",
          "items": {
            "type": "object",
            "properties": {
          "accelX":{"type": "number"},
          "accelY": {"type": "number"},
          "accelZ": {"type": "number"},
          "createDate": {"type": "string"},
          "dataFormat":{"type": "number"},
          "defaultBackground": {"type": "number"},
          "favorite": {"type": "boolean"},
          "humidity": {"type": "number"},
          "id": {"type": "string"},
          "measurementSequenceNumber": {"type": "number"},
          "movementCounter": {"type": "number"},
          "name": {"type": "string"},
          "pressure": {"type": "number"},
          "rssi": {"type": "number"},
          "temperature": {"type": "number"},
          "txPower":{"type": "number"},
          "updateAt": {"type": "string"},
          "voltage": {"type": "number"}
            }
        }   
      },
      "time": {"type": "string"}
    }
    }
    

    For each action I also enabled CORS and replaced the existing CORS headers.

    These two videos explains the entire process much better than the documentation and I hope it helps.

    Part 1

    Part 2