Skip to content

ServerlessReact.dev

Student Login
  • Welcome to the workshop
Before the workshop
The workshop
Before you head out

Dealing with a database

S3 is great for static files, but what if you want a little more?

For dynamic data, we'll use a database. DynamoDB integrates nicely with the Serverless ecosystem and doesn't require a lot of setup.

Choosing a database for your project in the real world is a matter of balancing various factors.

Create a DynamoDB table

DynamoDB stores data in tables. Each works as its own database and you can have as many as you want.

Exercise

Move into exercise-6 in the exercises repository.

Create a new YAML file to specify a new DynamoDB table.

# dynamodb.yml
Resources:
ItemsTable:
Type: "AWS::DynamoDB::Table"
Properties:
AttributeDefinitions:
- AttributeName: itemId
AttributeType: S
KeySchema:
- AttributeName: itemId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: ${self:provider.environment.ITEMS_TABLE}

This creates a DynamoDB table with an identifier.

Add this table to your serverless config. Don't forget to install serverless-resources-env

# serverless.yml
resources:
- ${file(dynamodb.yml)}
plugin:
- serverless-resources-env

You can now access your DynamoDB's name as process.env.CF_ItemsTable

You'll need to grant access and specify the name. I recommend starting with liberal permissions.

# serverless.yml
provider:
environment:
ITEMS_TABLE: ${self:service}-items-${self:provider.stage}
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.ITEMS_TABLE}"

Install the simple-dynamodb package. It's a thin wrapper I built to help with common DynamoDB operations.

Solution

https://github.com/Swizec/serverless-workshop-exercises/commit/2f0f402eba4e62963936ca9be0c16e115cfe6edc#diff-5798e57392a10541e65cb482320a0501

Write to your table

Before you can read from a table, you have to store something. We're going to treat every write as an upsert for simplicity.

Create item on first call, update on subsequent calls.

Exercise

Create a new lambda function handling a POST request. Accept a JSON payload and an itemId path param.

Save the JSON object in your table with code like this:

import * as db from "./dynamodb"
const updatedAt = new Date().toISOString()
// db.buildExpression and db.buildAttributes are helper functions to create DynamoDB queries
const item = await db.updateItem({
TableName: process.env.ITEMS_TABLE!,
Key: { itemId },
UpdateExpression: `SET ${db.buildExpression(body)}, updatedAt = :updatedAt`,
ExpressionAttributeValues: {
...db.buildAttributes(body),
":updatedAt": updatedAt,
},
ReturnValues: "ALL_NEW",
})

You can access saved values via item.Attributes. I recommend returning them.

Try your function

Leave payload empty for GET requests, valid JSON for POST.

Solution

https://github.com/Swizec/serverless-workshop-exercises/commit/8cb7d1617f5370948a093954a77c9f8fe4bf0868#diff-5798e57392a10541e65cb482320a0501

Read from your table

Reading from your table is easier. My wrapper provides a helpful db.getItem method.

Exercise

Create a new function that handles a GET request. Use a path param for the ID.

Use code like this to read from your table.

// read itemId from pathParameters
const item = await db.getItem({
TableName: process.env.ITEMS_TABLE!,
Key: { itemId },
})

Return item.Item if found. 404 status code if not.

Try your function

Leave payload empty for GET requests, valid JSON for POST.

Solution

https://github.com/Swizec/serverless-workshop-exercises/commit/6cafbbd4f6d832652f8880c218615b4327faeee7#diff-5798e57392a10541e65cb482320a0501

Did you enjoy this chapter?

Previous:
Save file to S3
Next:
Integrate with UI
Created bySwizecwith ❤️