node.jsdockeramazon-dynamodbaws-samamazon-dynamodb-local

AWS SAM Local + DynamoDB Local


I'm currently testing out AWS SAM with DynamoDB Local using Docker.

Here is the steps that I followed (mostly found in the internet)

  1. Create new docker network using docker network create local-dev.
  2. Run DynamoDB Local docker run -d -v "$PWD":/dynamodb_local_db -p 8000:8000 --network local-dev --name dynamodb amazon/dynamodb-local. Until this point, I'm being able to create and list tables using AWS CLI.
  3. Then, I proceed with running AWS SAM sam local start-api --docker-network local-dev. Everything looks okay.
  4. Invoked lambda.js, but it looks like no result for console.log(err)or console.log(data).

I'm not sure where could it be wrong. Please help me. Thank you in advance!

lambda.js

const services = require('./services.js');
const AWS = require('aws-sdk');

let options = {
    apiVersion: '2012-08-10',
    region: 'ap-southeast-1',
}

if(process.env.AWS_SAM_LOCAL) {
  options.endpoint = new AWS.Endpoint('http://localhost:8000')
}

const dynamoDB = new AWS.DynamoDB(options);

exports.getUser = async (event, context) => {

    let params = {};

    dynamoDB.listTables(params, (err, data) => {
        if(err) console.log(err)
            else console.log(data)
    })

    return true;
}

template.yaml

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


Parameters:

  FunctionsCodeBucket:
    Type: String
    Description: CodeBucket
  FunctionsCodeKey:
    Type: String
    Description: CodeKey
  FunctionsCodeVersion:
    Type: String
    Description: CodeVersion
  NodeEnv:
    Type: String
    Description: NodeEnv

Globals:
  Api:
    Cors:
      AllowMethods: "'OPTIONS,POST,GET,DELETE,PUT'"
      AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,Api-Key,api-key'"
      AllowOrigin: "'*'"
  Function:
    Timeout: 300
    Runtime: nodejs10.x
    MemorySize: 128
    CodeUri: ./ 

Resources:
  DevResources:
    Type: AWS::Serverless::Function 
    Properties:                      
      Handler: "index.routes"
      Environment:
        Variables:
          NODE_ENV: !Ref NodeEnv
          # REGION: !Ref "AWS::Region"

      Policies:
      - Version: '2012-10-17'
        Statement:
        - Action:
          - dynamodb:*
          Effect: Allow
          Resource: "*"
      Events:
        GetUser:
          Type: Api 
          Properties:
            Path: /user
            Method: get

Solution

  • You lambda function does not wait for dynamoDB.listTables operation. You can fix this issue by using promisified version of dynamoDB.listTables as follows:

    exports.getUser = async (event, context) => {
    
      let params = {};
    
      try {
        const resp = await dynamoDB.listTables(params).promise();
        console.log(resp);
      } catch (err) {
        console.log(err)
      }
    };
    
    

    Another thing that you will likely need to do is to assign a network alias to your dynamodb container (you can do that using --network-alias=<container_name> option) for example, let's set the alias to dynamodb

    docker run -d -v "$PWD":/dynamodb_local_db -p 8000:8000 --network local-dev --network-alias=dynamodb --name dynamodb amazon/dynamodb-local
    

    After that you can use this network alias in your lambda function:

    if(process.env.AWS_SAM_LOCAL) {
      options.endpoint = new AWS.Endpoint('http://dynamodb:8000')
    }