Search code examples
javascriptpythonamazon-web-servicesaws-lambdaaws-api-gateway

Website visitor counter showing up as "[object Object]"


I am trying to setup a view counter for the AWS #CloudResumeChallenge. I am following the instructions on these two youtube videos:

I am also using both of these gentlemen's GitHub repos as references:

I have followed the video's instructions, step-by-step; however, when I load my cloud resume at https://justinhenson.cloud/, the visitor counter is showing "[object Object]" and upon inspecting the element, the original line in my index.html file changes from this:

<div id="visitors" class="visitors"> You are visitor number<span id="visits"></span><span id="loader" class="loader"></span></div>

To this:

<div id="visitors" class="visitors"> You are visitor number<span id="visits">[object Object]</span><span id="loader" class="loader" style="display: none;"></span></div><span id="visits">[object Object]</span><span id="loader" class="loader" style="display: none;"></span></div>

Also, as you can see in my scripts.js file below, I have pointed the pathway to the AWS API Gateway to trigger the lambda handler:

"use strict";

$(document).ready(() => {
    $.post('https://pl8h7bxm3j.execute-api.us-east-1.amazonaws.com/Prod/visit/')
    .done(visitor_counter => {
        $('#loader').hide();
        $('#visits').text(visitor_counter);
    })
    .fail(e => {
        console.log('Error');
        console.log(e);
    });
});

I have tested my API Gateway with the Postman app and it returns the count without error:

enter image description here

I followed both of the instructions in the videos by enabling the CORS policy in my lambda handler in the "app.py" file to be wide open to reduce complexity:

import json
import boto3

dynamodb = boto3.resource('dynamodb', region_name='us-east-1')

table = dynamodb.Table('resume-website-app-tbl')

def lambda_handler(event, context):
    response = table.get_item(
        Key = {
            'ID':'visits'
        }
    )
    
    visit_count = response['Item']['counter'] 
    visit_count = str(int(visit_count) + 1)
    
    response = table.put_item(
        Item = {
            'ID':'visits',
            'counter': visit_count
        }
    )

    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Headers': '*',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': '*'
        },
        'body': json.dumps({'visit_count': visit_count})
    }

Below is my template.yaml file that I built with SAM that contains the configuration for my CloudFront Distribution under "MyDistribution" and the configuration for MyLambdaFunction:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  cloud-resume

  Sample SAM Template for cloud-resume

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  ResumeWebsite:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: PublicRead
      WebsiteConfiguration:
        IndexDocument: index.html 
      BucketName: justinhenson-cloud-resume-website

  MyRoute53Record:
    Type: "AWS::Route53::RecordSetGroup"
    Properties:
      HostedZoneId: Z01881203GO4SRLRJE2CO 
      RecordSets:
        - Name: justinhenson.cloud 
          Type: A
          AliasTarget:
            HostedZoneId: Z2FDTNDATAQYW2
            DNSName: !GetAtt MyDistribution.DomainName

  MyCertificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: justinhenson.cloud 
      ValidationMethod: DNS

  MyDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        ViewerCertificate:
            AcmCertificateArn: !Ref MyCertificate
            SslSupportMethod: sni-only
        Aliases: 
          - justinhenson.cloud
        DefaultCacheBehavior:
          ViewerProtocolPolicy: redirect-to-https
          TargetOriginId: justinhenson-cloud-resume-website.s3.us-east-1.amazonaws.com
          DefaultTTL: 0
          MinTTL: 0
          MaxTTL: 0
          ForwardedValues:
            QueryString: false
        Origins:
          - DomainName: justinhenson-cloud-resume-website.s3.us-east-1.amazonaws.com
            Id: justinhenson-cloud-resume-website.s3.us-east-1.amazonaws.com
            CustomOriginConfig:
              OriginProtocolPolicy: http-only
        Enabled: "true"
        DefaultRootObject: index.html

  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties: 
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement:
          - Sid: PublicReadForGetBucketObjects
            Effect: Allow
            Principal: "*"
            Action: "s3:GetObject"
            Resource: !Join
              - ""
              - - "arn:aws:s3:::"
                - !Ref ResumeWebsite
                - /*
      Bucket: !Ref ResumeWebsite

  MyDynamoDBTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: resume-website-app-tbl
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: "ID"
          AttributeType: "S"
      KeySchema:
        - AttributeName: "ID"
          KeyType: "HASH"

  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Policies:
        - DynamoDBCrudPolicy:
            TableName: resume-website-app-tbl
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        Visits:
          Type: Api
          Properties:
            Path: /visit
            Method: post

  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

I sincerely appreciate any help anyone can offer in shedding some light on this issue. Thanks.


Solution

  • You are trying to put all HTTP response object to html, so you get [object Object]

    $(document).ready(() => {
        $.post('https://pl8h7bxm3j.execute-api.us-east-1.amazonaws.com/Prod/visit/')
        .done(res => {
            $('#loader').hide();
            $('#visits').text(res.visit_count);
        })
        .fail(e => {
            console.log('Error');
            console.log(e);
        });
    });