Configure DynamoDB in Serverless
We are now going to start creating our resources through our serverless.yml. Starting with DynamoDB.
Create the Resource
To do this, let’s create a directory where we will keep all the resources for our infrastructure.
$ mkdir resources/
And add the following to resources/dynamodb-table.yml.
Resources:
  NotesTable:
    Type: AWS::DynamoDB::Table
    Properties:
      # Generate a name based on the stage
      TableName: ${self:custom.stage}-notes
      AttributeDefinitions:
        - AttributeName: userId
          AttributeType: S
        - AttributeName: noteId
          AttributeType: S
      KeySchema:
        - AttributeName: userId
          KeyType: HASH
        - AttributeName: noteId
          KeyType: RANGE
      # Set the capacity based on the stage
      ProvisionedThroughput:
        ReadCapacityUnits: ${self:custom.tableThroughput}
        WriteCapacityUnits: ${self:custom.tableThroughput}
Let’s quickly go over what we are doing here.
- 
    
We are describing a DynamoDB table resource called
NotesTable. - 
    
The table name is based on the stage we are deploying to -
${self:custom.stage}-notes. The reason this is dynamically set is because we want to create a separate table when we deploy to a new stage (environment). So when we deploy todevwe will create a DynamoDB table calleddev-notesand when we deploy toprod, it’ll be calledprod-notes. This allows us to clearly separate the resources (and data) we use in our various environments. - 
    
We are also configuring the two attributes of our table as
userIdandnoteId. - 
    
Finally, we are provisioning the read/write capacity for our table through a couple of custom variables as well. We will be defining this shortly.
 
Add the Resource
Now let’s add a reference to this resource in our project.
Add the following to the bottom of our serverless.yml.
# Create our resources with separate CloudFormation templates
resources:
  # DynamoDB
  - ${file(resources/dynamodb-table.yml)}
And replace the custom: block at the top of our serverless.yml with the following:
custom:
  # Our stage is based on what is passed in when running serverless
  # commands. Or fallsback to what we have set in the provider section.
  stage: ${opt:stage, self:provider.stage}
  # Set our DynamoDB throughput for prod and all other non-prod stages.
  tableThroughputs:
    prod: 5
    default: 1
  tableThroughput: ${self:custom.tableThroughputs.${self:custom.stage}, self:custom.tableThroughputs.default}
  # Load our webpack config
  webpack:
    webpackConfig: ./webpack.config.js
    includeModules: true
We added a couple of things here that are worth spending some time on:
- 
    
We first create a custom variable called
stage. You might be wondering why we need a custom variable for this when we already havestage: devin theprovider:block. This is because we want to set the current stage of our project based on what is set through theserverless deploy --stage $STAGEcommand. And if a stage is not set when we deploy, we want to fallback to the one we have set in the provider block. So${opt:stage, self:provider.stage}, is telling Serverless to first look for theopt:stage(the one passed in through the command line), and then fallback toself:provider.stage(the one in the provider block. - 
    
Now we want to configure how we provision the read/write capacity for our table. Specifically, we want to let our production environment have a higher throughput than our dev (or any other non-prod environment). To do this we created a custom variable called
tableThroughputs, that has two separate settings calledprodanddefault. Theprodoption is set to5whiledefault(which will be used for all non-prod cases) is set to1. - 
    
Finally, to implement the two options we use
tableThroughput: ${self:custom.tableThroughputs.${self:custom.stage}, self:custom.tableThroughputs.default}. This is creating a custom variable calledtableThroughput(which we used in our DynamoDB resource above). This is set to look for the relevant option in thetableThroughputsvariable (note the plural form). So for example, if we are in prod, the throughput will be based onself:custom.tableThroughputs.prod. But if you are in a stage calledalphait’ll be set toself:custom.tableThroughputs.alpha, which does not exist. So it’ll fallback toself:custom.tableThroughputs.default, which is set to1. 
A lot of the above might sound tricky and overly complicated right now. But we are setting it up so that we can automate and replicate our entire setup with ease.
We are also going to make a quick tweak to reference the DynamoDB resource that we are creating.
Replace the iamRoleStatements: block in your serverless.yml with the following.
  # These environment variables are made available to our functions
  # under process.env.
  environment:
    tableName:
      Ref: NotesTable
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      # Restrict our IAM role permissions to
      # the specific table for the stage
      Resource:
        - "Fn::GetAtt": [ NotesTable, Arn ]
Make sure to copy the indentation properly. These two blocks fall under the provider block and need to be indented as such.
A couple of interesting things we are doing here:
- 
    
The
environment:block here is basically telling Serverless Framework to make the variables available asprocess.envin our Lambda functions. For example,process.env.tableNamewould be set to the DynamoDB table name for this stage. We will need this later when we are connecting to our database. - 
    
For the
tableNamespecifically, we are getting it by referencing theNotesTableresource that we created above. By using a reference we allow Serverless Framework to figure out the exact name of our resource and use it. - 
    
For the case of our
iamRoleStatements:we are now specifically stating which table we want to connect to. This block is telling AWS that these are the only resources that our Lambda functions have access to. 
Finally, we need to make a quick change to our code. We were hardcoding the region in the DynamoDB lib.
Remove the following line from the top of libs/dynamodb-lib.js.
AWS.config.update({ region: "us-east-1" });
Commit Your Code
Let’s commit the changes we’ve made so far.
$ git add .
$ git commit -m "Adding our DynamoDB resource"
Next, let’s add our S3 bucket for file uploads.
If you liked this post, please subscribe to our newsletter, give us a star on GitHub, and follow us on Twitter.
For help and discussion
Comments on this chapter