Creating Serverless Backend using AWS Lambda and DynamoDB
A serverless backend lets you store and serve data without ever managing a server, and in this post we’ll build one with a Lambda function that creates and lists items in DynamoDB.
At a high level, the setup looks like this:
Everything starts with the DynamoDB table, which I created using the Java SDK with the code below. The same code can be found in the AWS documentation.
The one change I made was setting the provisioned resources to 1, since we’re just experimenting here and don’t need much capacity.
public void createTable(String name) {
CreateTableRequest request = new CreateTableRequest()
.withAttributeDefinitions(new AttributeDefinition(
"Name", ScalarAttributeType.S))
.withKeySchema(new KeySchemaElement("Id", KeyType.HASH))
.withProvisionedThroughput(new ProvisionedThroughput(
new Long(1), new Long(1)))
.withTableName(name);
final AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder.defaultClient();
try {
CreateTableResult result = ddb.createTable(request);
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
System.exit(1);
}
}
Since DynamoDB is a NoSQL database, I also created a model to shape the items we’ll store. The model is simple: a person’s name, surname and addresses.
package model;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import java.util.List;
@DynamoDBTable(tableName = "personTable")
public class Person {
private String id;
private String name;
private String surname;
private List<String> addresses;
@DynamoDBHashKey(attributeName = "Id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@DynamoDBAttribute(attributeName = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@DynamoDBAttribute(attributeName = "surname")
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
@DynamoDBAttribute(attributeName = "addresses")
public List<String> getAddresses() {
return addresses;
}
public void setAddresses(List<String> addresses) {
this.addresses = addresses;
}
}
Add item function:
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import model.Person;
public class LambdaMain implements RequestHandler<Person,String> {
public String handleRequest(Person person, Context context) {
final AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder.defaultClient();
final DynamoDBMapper mapper = new DynamoDBMapper(ddb);
try {
mapper.save(person);
} catch (ResourceNotFoundException e) {
System.exit(1);
} catch (AmazonServiceException e) {
System.err.println(e.getMessage());
System.exit(1);
}
return "OK";
}
}
With the function written, let’s invoke it from the Lambda console.
Yes, I know you can’t find James Holden on Earth; maybe we should write Rocinante :).
{
"id": "f01349dd-332d-4937-b3ae-9b50b995d2ef",
"name": "James",
"surname": "Holden",
"addresses": ["Earth"]
}
Show item function:
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import model.Person;
import java.util.List;
import java.util.Map;
public class LambdaMain implements RequestHandler<Map<String,Object>,List<Person>> {
public List<Person> handleRequest(Map<String,Object> input, Context context) {
final AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder.defaultClient();
final DynamoDBMapper mapper = new DynamoDBMapper(ddb);
List<Person> personList = null;
try {
personList = mapper.scan(Person.class, new DynamoDBScanExpression());
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
return personList;
}
}
Now let’s deploy and test this show method.
To expose it, I created a new stage called prod on API Gateway and published my methods there, and API Gateway handed me a link to invoke them.
webischia@aws:~$ curl https://57l5o766j7.execute-api.us-west-2.amazonaws.com/prod/ | jq
{
"id": "1",
"name": "lambda",
"surname": "aws"
},
{
"id": "234c1579-6cb4-4488-aa69-961c49a7736b",
"name": "Fahri",
"surname": "YARDIMCI",
"addresses": [
"Turkey"
]
},
{
"id": "4b591dff-7c1d-41c9-8e4a-8d1f57c52cfd",
"name": "Jeff",
"surname": "Bezos",
"addresses": [
"Earth",
"Mars",
"Belt"
]
},
{
"id": " f01349dd-332d-4937-b3ae-9b50b995d2ef",
"name": "James",
"surname": "Holden",
"addresses": [
"Earth"
]
}
]
Conclusion:
Building a serverless backend with Lambda is both fun and fast. You don’t even have to think about servers or the security of the underlying infrastructure.

