How to Store and Fetch From DynamoDB With AWS Lambda

Adding database storage to your serverless setup

Luke Mwila
May 27, 2019 · 6 min read
Image for post
Image for post
Photo by the author.

One of the biggest benefits of AWS Lambda is how easily it integrates with other AWS technologies. In this article, I’ll be giving a quick guide on how to create a DynamoDB table and deploy functions that will allow us to store and fetch records from our table. For this walkthrough, we’ll be building a to-do-list API using the serverless framework.

If this is your first time working with the serverless framework, you can quickly follow the setup steps I outlined in a previous article. It should only take you a few minutes.

If you enjoy the post, feel free to buy me a coffee here ☕️ 😃 .

Create New SLS Project

The first step is to initialise a new serverless project in a selected folder. You can do so with the following command:

sls create --template aws-nodejs-typescript --path aws-lambda-with-dynamodb

That will set up our app structure with some boilerplate code, including a basic lambda function.

Install Dependencies and Update Manifest File

You can start by running npm i to install any missing dependencies. Additional packages that we’ll be making use of are uuid (to generate unique IDs for the records in the table of our database) and the serverless-offline plugin (so we can emulate AWS Lambda and API Gateway locally).

npm i —-save aws-lambda uuid
npm i -D @types/uuid serverless-offline

Once you have installed those dependencies, you can add a script to your package.json file to trigger the start lifecycle event to run a command that starts the serverless-offline emulator on a specific port (4000 in my case).

"scripts": {
"start": "sls offline start --port 4000",
"test": "echo \"Error: no test specified\" && exit 1"
}

Resource Configuration

Inside your serverless app directory, create a folder called Resources. Inside this folder, we’ll add a YAML file called dynamo-db.yml. In here, we’ll add the AWS::DynamoDB::Table resource that creates a DynamoDB table.

What are the properties being defined in this file?

  1. We set a description for the DynamoDB table resource to be created (ToDoListTable).
  2. We define the actual name (TableName) of the DynamoDB table as to-do-list.
  3. We set the AttributeDefinitions for the table that describe the key schema and indexes. Our table has an AttributeName of id and an AttributeType of String (S).
  4. We define the KeySchema that specifies the attributes that make up the primary key for the table. In this case, it will be the id. The attributes in the KeySchema property must also be defined in the AttributeDefinitions property.
  5. Lastly, we provision the read and write capacity for our to-do-list table with the ProvisionedThroughput property. The ReadCapacityUnits and WriteCapacityUnits define the maximum strongly consistent reads consumed per second and the maximum writes consumed per second before DynamoDB throws a ThrottlingException. You can read more about these properties and the implications of their settings on Amazon Web Services.

Service Configuration

Let’s turn our attention to the serverless.yml file. I’ll start by setting the region to eu-west-1, but you can go ahead and select a different region that best suits you.

To speed up local development, we are going to configure the serverless-offline plugin we installed earlier that will emulate AWS Lambda and API Gateway as follows:

plugins:
- serverless-webpack
- serverless-offline

Next up, let’s reference the DynamoDB resource that we just added like this:

resources:
# DynamoDB
- ${file(resources/dynamodb-table.yml)}

We want to give our lambda functions permission to interact with our DynamoDB table. These permissions are set via an AWS IAM Role and we can set the permission policy statements within this role via the provider.iamRoleStatements property. It is important to note that with this configuration, every function we deploy in this service will share these permissions.

iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem

For security purposes, we want to limit access permission of our lambda service to the to-do-list table resource, and so we can define that in the Resource property by specifying the resource using its ARN (Amazon Resource Name) like so:

Resource: "arn:aws:dynamodb:eu-west-1:accountId:table/to-do-list"

Make sure to include your AWS Account ID in place of the text accountId in the Resource property field.

While we’re still in serverless.yml, I’m going to go ahead and configure the lambda functions that will be used to generate the API endpoints that we’ll be querying to save new records in the table, as well as retrieving them.

functions:
saveToDoItem:
handler: handler.saveToDoItem
events:
- http:
method: post
path: save-to-do-item
cors: true
getToDoItem:
handler: handler.getToDoItem
events:
- http:
method: get
path: to-do-items/{id}
cors: true

Create Handler Functions

In our handler.ts file, I’m going to create a function named respond that wraps the response that gets sent back to the client. This function takes two parameters:

  1. The data we’re sending as a response.
  2. The HTTP response status code.
export const respond = (fulfillmentText: any, statusCode: number): any => {
return {
statusCode,
body: JSON.stringify(fulfillmentText),
headers: {
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json"
}
};
};

Next, I’m going to create the two handlers that I defined as functions in the serverless.yml file.

These two functions call other functions that don’t exist yet, so let’s go ahead and create them. In your app directory, you can create another folder called dynamodb-actions, and in there create an index.ts file. In our index.ts file, we’re going to instantiate the DynamoDB DocumentClient object from the JavaScript AWS SDK.

const dynamoDB = new AWS.DynamoDB.DocumentClient();

There should be no need for you to install it on a project level since the Lambda environment ships with it. However, I would advise you to install the AWS SDK globally on your local machine for local development purposes.

The first function we will create is saveItemInDB, which will take the two relevant arguments required to create an item in our to-do-list table. We then create the parameters (params) for the DocumentClient’s put function by specifying the table name and the item we want to store in the table.

The second function we are going to create is getItemFromDB. We will use this function to retrieve an item from the to-do-list table by specifying the table name as well as the item ID as a key in the parameters (params) for the DocumentClient’s get function.

/** get a to-do item from the db table */
export function getItemFromDB(id: string) {
const params = {
TableName: "to-do-list",
Key: {
id
}
};
return dynamoDB
.get(params)
.promise()
.then(res => res.Item)
.catch(err => err);
}

We can then import these two functions to the handler.ts file to resolve any errors that we were previously getting because they didn’t exist.

Test AWS Lambda Functions

Finally, we get to test out our lambda functions. To do this, we can use an API testing tool like Insomnia or Postman. I’m going to be using the latter.

Image for post
Image for post
Postman API development environment.

Once you’ve decided on an API testing tool, you can start your serverless-offline emulator by running npm start in the terminal.

To test my handler.saveToDoItem, I make a POST request to http://localhost:4000/save-to-do-item and I get the following response:

{
"created": {
"item": "Write new post",
"complete": false
}
}

To get the item’s ID, you can either modify your code for it to be included in the response or you can head over to the AWS Console and retrieve the record ID from the to-do-list table.

Image for post
Image for post
DynamoDB table in AWS console.

We can then use the ID of the newly created record to test that our handler.getToDoItem works as it should. To do this, we make a GET request to http://localhost:4000/to-do-items/{id} and should get a response like:

{
"id": "e843eafa-3468-4d84-b87e-2629c276543e",
"complete": false,
"item": "Have a glass of water"
}

If it’s all working well (which it should be), you can go ahead and deploy your service with the following command (if you didn’t already do so earlier):

sls deploy --aws-profile devProfile

Once you’re done with that, give yourself the small challenge of writing and deploying functions that allow you to update and delete records from your to-do-list table.

You can find the code for this article on GitHub.

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store