{"id":15619120,"url":"https://github.com/xavidop/alexa-nodejs-dynamo-local","last_synced_at":"2026-04-12T00:41:07.771Z","repository":{"id":44757535,"uuid":"256777246","full_name":"xavidop/alexa-nodejs-dynamo-local","owner":"xavidop","description":"Alexa Skill with Local DynamoDB ","archived":false,"fork":false,"pushed_at":"2022-01-26T15:15:55.000Z","size":924,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-04T16:18:24.768Z","etag":null,"topics":["alexa","alexa-custom-skill","alexa-sdk","alexa-skill","alexa-skills","alexa-skills-kit","aws","aws-cli","aws-lambda","docker-image","dynamodb","dynamodb-client","dynamodb-local","java","local-database","nodejs","npm","tool"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xavidop.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"xavidop","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-04-18T14:43:35.000Z","updated_at":"2020-11-21T19:01:51.000Z","dependencies_parsed_at":"2022-09-03T21:52:12.966Z","dependency_job_id":null,"html_url":"https://github.com/xavidop/alexa-nodejs-dynamo-local","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavidop%2Falexa-nodejs-dynamo-local","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavidop%2Falexa-nodejs-dynamo-local/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavidop%2Falexa-nodejs-dynamo-local/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xavidop%2Falexa-nodejs-dynamo-local/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xavidop","download_url":"https://codeload.github.com/xavidop/alexa-nodejs-dynamo-local/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246202672,"owners_count":20740007,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alexa","alexa-custom-skill","alexa-sdk","alexa-skill","alexa-skills","alexa-skills-kit","aws","aws-cli","aws-lambda","docker-image","dynamodb","dynamodb-client","dynamodb-local","java","local-database","nodejs","npm","tool"],"created_at":"2024-10-03T08:02:42.466Z","updated_at":"2026-04-12T00:41:07.731Z","avatar_url":"https://github.com/xavidop.png","language":"JavaScript","funding_links":["https://github.com/sponsors/xavidop"],"categories":[],"sub_categories":[],"readme":"# Alexa Skill with local DynamoDB \n\nMocking away dependencies — especially external dependencies — is common practice when writing tests or when you are developing locally.\nDependency injection typically makes it easy to provide a mock implementation for your dependencies, e.g. a database.\n\nIn this article we will discuss how to mocking a [DynamoDB](https://aws.amazon.com/dynamodb/).\n\nMocking database is a technique that allows you to set the desired database state in your tests to let specific data-sets ready for future test execution. \nUsing this technique, you can focus on getting the test data-sets ready once, and then use it on different test phases regarding the environments by using mocking.\nThis technique is also useful when you are writing your code on your local laptop.\nIn other words, Database Mocking is a simulation of a database either with few records or with an empty database.\n\nAlexa Skills can use DynamoDB to persist data between sessions. DynamoDB is a fully managed NoSQL database offered by Amazon Web Services.\n\n## Prerequisites\n\nHere you have the technologies used in this project\n1. Amazon Developer Account - [How to get it](http://developer.amazon.com/)\n2. AWS Account - [Sign up here for free](https://aws.amazon.com/)\n3. ASK CLI - [Install and configure ASK CLI](https://developer.amazon.com/es-ES/docs/alexa/smapi/quick-start-alexa-skills-kit-command-line-interface.html)\n4. AWS CLI - [Install and configure AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)\n5. Node.js v10.x\n6. Java Runtime Environment (JRE) version 6.x or newer\n7. Visual Studio Code\n8. npm Package Manager\n\nThe Alexa Skills Kit Command Line Interface (ASK CLI) is a tool for you to manage your Alexa skills and related resources, such as AWS Lambda functions.\nWith ASK CLI, you have access to the Skill Management API, which allows you to manage Alexa skills programmatically from the command line.\nIf you want how to create your Skill with the ASK CLI, please follow the first step explained in my [Node.js Skill](https://github.com/xavidop/alexa-nodejs-lambda-helloworld) sample. Let's start!\n\n## Creating local DynamoDB\n\nIn this project we are going to use the npm package `dynamodb-localhost`. This library works as a wrapper for AWS DynamoDB Local, intended for use in devops. This library is capable of downloading and installing the DynamoDB Local with a simple set of commands, and pass optional attributes defined in [DynamoDB Local Documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html).\n\nIf you are using Docker, this npm library executes exactly the same commands but in a different way. The Docker image and this library will run the same [executable jar file](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html).\n\nWe will use this library in order to make it easier mocking the DynamoDB.\n\nWe need to execute these steps in order to run our local DynamoDB.\n* The first thing we need is to install this local DynamoDB using the library. This step will be achieved by running the method `dynamodbLocal.install()`. This method will download the latest version of the official jar file that you can find above.\n* Once the DynamoDB is installed locally, now we can start it running the method `dynamodbLocal.start(options)`. To run these two steps synchronously we will use the npm package `synchronized-promise`. The `options` object has these properties:\n  * **port:** Port to listen on. Default: 8000\n  * **cors:**  Enable CORS support (cross-origin resource sharing) for JavaScript. You must provide a comma-separated \"allow\" list of specific domains. The default setting for cors is an asterisk (*), which allows public access.\n  * **inMemory:** DynamoDB; will run in memory, instead of using a database file. When you stop DynamoDB;, none of the data will be saved. Note that you cannot specify both dbPath and inMemory at once. \n  * **dbPath:** The directory where DynamoDB will write its database file. If you do not specify this option, the file will be written to the current directory. Note that you cannot specify both dbPath and inMemory at once. For the path, current working directory is \u003cprojectroot\u003e/node_modules/dynamodb-localhost/dynamob. For example to create \u003cprojectroot\u003e/node_modules/dynamodb-localhost/dynamob/\u003cmypath\u003e you should specify '\u003cmypath\u003e/' with a forwardslash at the end. \n  * **sharedDb:** DynamoDB will use a single database file, instead of using separate files for each credential and region. If you specify sharedDb, all DynamoDB clients will interact with the same set of tables regardless of their region and credential configuration.\n  * **delayTransientStatuses:** Causes DynamoDB to introduce delays for certain operations. DynamoDB can perform some tasks almost instantaneously, such as create/update/delete operations on tables and indexes; however, the actual DynamoDB service requires more time for these tasks. Setting this parameter helps DynamoDB simulate the behavior of the Amazon DynamoDB web service more closely. (Currently, this parameter introduces delays only for global secondary indexes that are in either CREATING or DELETING status.)\n  * **optimizeDbBeforeStartup:** Optimizes the underlying database tables before starting up DynamoDB on your computer. You must also specify -dbPath when you use this parameter.\n  * **heapInitial:** A string which sets the initial heap size e.g., heapInitial: '2048m'. This is input to the java -Xms argument \n  * **heapMax:**  A string which sets the maximum heap size e.g., heapMax: '1g'. This is input to the java -Xmx argument\n* Once we have the DynamoDB running on `http:localhost:8000` we have to create a new DynamoDB client which will connect to this local DynamoDB\n\nSo if you want to keep the information between your executions you can set `inMemory` to `false` and additionally, you can specify the `dbPath` where the data will be stored.\n\nThis code is located in `utilities/utils.js` file:\n\n```javascript\n\n  function getLocalDynamoDBClient(options) {\n\n        //Javascript Promise used for installing and starting local DynamoDB\n        const initializeClient = () =\u003e {\n            return new Promise((resolve, reject) =\u003e {\n                dynamodbLocal.install(() =\u003e {\n                    if (!options) reject(new Error('no options passed in!'))\n                    dynamodbLocal.start(options);\n                    resolve();\n                });    \n            })\n        };\n\n        //install \u0026 start synchronously\n        let syncInitialization = sp(initializeClient)\n        syncInitialization();\n\n        //configuration for creating a DynamoDB client that will connect to the local instance\n        AWS.config.update({\n          region: 'local',\n          endpoint: 'http://localhost:' + options.port,\n          accessKeyId: 'fake',\n          secretAccessKey: 'fake',\n        });\n    \n        return new AWS.DynamoDB();\n  }\n\n```\n\n## Using local DynamoDB\n\nNow we have our DynamoDB running on our laptop and a client configured ready to connect to it. It is time to set up the Alexa Skill to use this client.\nBefore this, it is important to notice that a very powerful feature of the new Alexa SDK, is the ability to save session data to DynamoDB with one line of code. \nBut in order to activate this feature, you have to tell to the ASK persistence adapter you are going to use and which client will use this adapter.\nWe need to add the npm package `ask-sdk-dynamodb-persistence-adapter` to create our persistence adapter.\n\nThis code is located in `utilities/utils.js` file:\n\n```javascript\n\n  function getPersistenceAdapter(tableName, createTable, dynamoDBClient) {\n\n    let options = {\n        tableName: tableName,\n        createTable: createTable,\n        partitionKeyGenerator: (requestEnvelope) =\u003e {\n          const userId = Alexa.getUserId(requestEnvelope);\n          return userId.substr(userId.lastIndexOf(\".\") + 1);\n        }\n    }\n    //if a DynamoDB client is specified, this adapter will use it. e.g. the one that will connect to our local instance\n    if(dynamoDBClient){\n        options.dynamoDBClient = dynamoDBClient\n    }\n\n   return new DynamoDbPersistenceAdapter(options);\n  }\n\n```\nOnce we have the local DynamoDB running, the client created, the persistence adapter created and using this client, it is time to set the adapter to our Skill.\n\nThis is how our `index.js` looks like:\n\n```javascript\n\n  var local = process.env.DYNAMODB_LOCAL\n  let persistenceAdapter;\n  //depending if we have enabled the local DynamoDB, we create de persistence adapter with or without local client\n  if(local === 'true'){\n    let options = { port: 8000 }\n    let dynamoDBClient = getLocalDynamoDBClient(options); \n    persistenceAdapter = getPersistenceAdapter(\"exampleTable\", true, dynamoDBClient);\n  }else{\n    persistenceAdapter = getPersistenceAdapter(\"exampleTable\", true);\n  }\n\n  /**\n   * This handler acts as the entry point for your skill, routing all request and response\n   * payloads to the handlers above. Make sure any new handlers or interceptors you've\n   * defined are included below. The order matters - they're processed top to bottom \n   * */\n  exports.handler = Alexa.SkillBuilders.custom()\n      .addRequestHandlers(\n          LaunchRequestHandler,\n          HelloWorldIntentHandler,\n          HelpIntentHandler,\n          CancelAndStopIntentHandler,\n          FallbackIntentHandler,\n          SessionEndedRequestHandler,\n          IntentReflectorHandler)\n      .addErrorHandlers(\n          ErrorHandler)\n      .withPersistenceAdapter(persistenceAdapter)\n      .addRequestInterceptors(\n          LocalisationRequestInterceptor)\n      .addResponseInterceptors(\n          SaveAttributesResponseInterceptor)\n      .lambda();\n\n```\n\nFinally, we have an example of persisting the data in our `SaveAttributesResponseInterceptor` interceptor located in `interceptors` folder:\n\n```javascript\n\n  const Alexa = require(\"ask-sdk-core\");\n\n  module.exports = {\n    SaveAttributesResponseInterceptor: {\n      async process(handlerInput, response) {\n        if (!response) return; \n\n        const { attributesManager, requestEnvelope } = handlerInput;\n\n        console.log(\n          \"Saving to persistent storage:\" + JSON.stringify(requestEnvelope)\n        );\n        attributesManager.setPersistentAttributes(requestEnvelope);\n        await attributesManager.savePersistentAttributes();\n      },\n    },\n  };\n\n```\n\nAs you can see, the interceptor above is storing in the DynamoDB the incoming request. This is just a silly example used to show you how it works.\n\n## Running the DynamoDB locally with Visual Studio Code\n\nThe `launch.json` file in `.vscode` folder has the configuration for Visual Studio Code which allow us to run our lambda locally:\n\n```json\n\n  {\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n          {\n              \"type\": \"node\",\n              \"request\": \"launch\",\n              \"name\": \"Launch Skill\",\n              \"env\": {\n                  \"DYNAMODB_LOCAL\": \"true\"\n              },\n              // Specify path to the downloaded local adapter(for nodejs) file\n              \"program\": \"${workspaceRoot}/lambda/custom/local-debugger.js\",\n              \"args\": [\n                  // port number on your local host where the alexa requests will be routed to\n                  \"--portNumber\", \"3001\",\n                  // name of your nodejs main skill file\n                  \"--skillEntryFile\", \"${workspaceRoot}/lambda/custom/index.js\",\n                  // name of your lambda handler\n                  \"--lambdaHandler\", \"handler\"\n              ]\n          }\n      ]\n  }\n\n```\nThis configuration file will execute the following command:\n\n```bash\n\n  node --inspect-brk=28448 lambda\\custom\\local-debugger.js --portNumber 3001 --skillEntryFile lambda/custom/index.js --lambdaHandler handler\n\n```\n\nThis configuration uses the `local-debugger.js` file which runs a [TCP server](https://nodejs.org/api/net.html) listening on http://localhost:3001\n\nFor a new incoming skill request a new socket connection is established.\nFrom the data received on the socket the request body is extracted, parsed into JSON and passed to the skill invoker's lambda handler.\nThe response from the lambda handler is parsed as a HTTP 200 message format as specified [here](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#http-header-1)\nThe response is written onto the socket connection and returned.\n\nAfter configuring our launch.json file and understanding how the local debugger works, it is time to click on the play button:\n\n![image](img/run.png)\n\nAfter executing it, you can send Alexa POST requests to http://localhost:3001.\n\n**NOTE:** If you want to start the local DynamoDB you have to set to `true` the environment variable `DYNAMODB_LOCAL` in this file.\n\n## Debugging and testing the Skill with Visual Studio Code\n\nFollowing the steps before, now you can set up breakpoints wherever you want inside all JS files in order to debug your skill. \n\nIn my post talking about [Node.js Skill](https://github.com/xavidop/alexa-nodejs-lambda-helloworld) you can see how to test your Skill either directly with Alexa Developer Console or locally with Postman.\n\n## Checking the local DynamoDB\n\nWhen we are running the DynamoDB locally, this local instance we will set up a shell in http://localhosta:8000/shell\n\n![image](img/shell.png)\n\nIn that shell we can execute queries in order to check the content of our local database. These are some example of queries you can do:\n\n1. Get all the content of our table:\n\n```javascript\n\n  //GET ALL VALUES FROM TABLE\n\n  var params = {\n      TableName: 'exampleTable',\n\n      Select: 'ALL_ATTRIBUTES', // optional (ALL_ATTRIBUTES | ALL_PROJECTED_ATTRIBUTES |\n                                //           SPECIFIC_ATTRIBUTES | COUNT)\n      ConsistentRead: false, // optional (true | false)\n      ReturnConsumedCapacity: 'NONE', // optional (NONE | TOTAL | INDEXES)\n  };\n\n\n  AWS.config.update({\n    region: \"local\",\n    endpoint: \"http://localhost:8000\",\n    accessKeyId: \"fake\",\n    secretAccessKey: \"fake\"\n  });\n\n  var dynamodb = new AWS.DynamoDB();\n\n\n  dynamodb.scan(params, function(err, data) {\n      if (err) ppJson(err); // an error occurred\n      else ppJson(data); // successful response\n  });\n\n```\n\nThen we can show the data of the table:\n\n![image](img/table.png)\n\n\n2. Get the information of our table:\n\n```javascript\n\n  //GET TABLE INFORMATION\n  var params = {\n      TableName: 'exampleTable',\n  };\n\n  AWS.config.update({\n    region: \"local\",\n    endpoint: \"http://localhost:8000\",\n    accessKeyId: \"fake\",\n    secretAccessKey: \"fake\"\n  });\n\n  var dynamodb = new AWS.DynamoDB();\n\n  dynamodb.describeTable(params, function(err, data) {\n      if (err) ppJson(err); // an error occurred\n      else ppJson(data); // successful response\n  });\n\n```\n\nNow we can show the information of our table:\n\n![image](img/info.png)\n\nThese queries are using the [AWS SDK for JavaScript](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/dynamodb-examples.html). \n\nThis local DynamoDB is accessible by the AWS CLI as well. Before using the CLI, we need to create a `fake` profile that will use the region, accessKeyId and secretAccessKey used by our local database and client. So in our `~/.aws/credentials` we have to create the `fake` profile:\n\n```bash\n\n  [fake]\n  aws_access_key_id=fake\n  aws_secret_access_key=fake\n\n```\n\nAnd in our `~/.aws/config` we set the local region for our `fake` profile:\n\n```bash\n\n  [fake]\n  region = local\n\n```\n\nAfter creating it, now we can execute queries using the AWS CLI using our `fake` profile:\n\n```bash\n\n  aws dynamodb list-tables --endpoint-url http://localhost:8000 --region local --profile fake\n\n```\n\nThis command will return a list of tables in our local database:\n\n```json\n\n  {\n      \"TableNames\": [\n          \"exampleTable\"\n      ]\n  }\n\n```\n\nYou can find more information about how to make queries with the AWS CLI [here](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/index.html)\n\n## Extra\n\nOf course if you do not want to use the npm package `dynamodb-localhost`, AWS offers to us other ways to run a local instance.\nThese ways are:\n1. Docker image. All info [here](https://hub.docker.com/r/amazon/dynamodb-local).\n2. Maven dependency if you have your skill in java using Maven or Gradle. All info [here](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.Maven.html)\n\n\nAt the end, those solutions will run exactly the same as npm package `dynamodb-localhost` but in some different ways. Choose the one that fits you better!\n\n\n## Conclusion \n\nThis was a basic tutorial to mock a DynamoDB with our Alexa Skills using Node.js.\nWith this technique, you can easily make changes to your unit test data and run experiments. This is will make your tests more believable with \"real\" data and a \"real\" environment.\n\nHow many of you have had an issue with production where it works on staging, but doesn't work on production and the source code is the same in both environments.\n\nThis is one example of being able to take/save data from/to a local DynamoDB (with the query above) run it to find out issues, how it works, etc.\n\nIn the long run, this makes your unit tests even more valuable to you.\n\nI hope this example project is useful to you.\n\nThat's all folks!\n\nHappy coding!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxavidop%2Falexa-nodejs-dynamo-local","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxavidop%2Falexa-nodejs-dynamo-local","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxavidop%2Falexa-nodejs-dynamo-local/lists"}