Introduction to Scanamo Version – 1.0.0-M11

Reading Time: 2 minutes

Scanamo

Scanamo is a library to make use of DynamoDB with Scala.

In this blog, we are going to work on latest version 1.0.0-M11

So, let’s get it started-

1. Add Library Dependencies: –

Setup sbt project and add these library dependencies

libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.5.25"
libraryDependencies += "org.scanamo" %% "scanamo-alpakka" % "1.0.0-M11"
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % "2.5.25"

2. Setup application.conf: –

Create application.conf file and place it inside the src\main\resources\

akka.stream.alpakka.dynamodb {
  region = "eu-west-1"
  host = "localhost"
  port = 8000
  tls = false
  parallelism = 32
  credentials {
    access-key-id = "dummy-access-key"
    secret-key-id = "dummy-secret-key"
  }
}

3. Create Case Class And DAO: –

Create case class as it is required to map it with DynamoDB table.

case class User(id: Long, name: String, email: String)

Below is a trait which represent DAO, DAO is DATA ACCESS OBJECT

trait UserDAO {

 def addUser(user: User): Future[Option[Either[DynamoReadError, User]]]

 def getUser(email: String): Future[Option[Either[DynamoReadError, User]]]

 def updateUserName(email: String, updatedName:String): Future[Either[DynamoReadError, User]]

 def deleteUser(email: String): Future[String]
}

4. Implement UserDAO: –

Now, implement DAO and provide body to the abstract method as shown, Here we are using scanamo library to perform curd operation with DynamoDB.

class DynamoDBImplementation(tableName: String)(
  implicit val actorMaterializer: ActorMaterializer,
  val dynamoClient: DynamoClient,
  val executionContext: ExecutionContext
) extends UserDAO {

  private lazy val scanamoAlpakka: ScanamoAlpakka = ScanamoAlpakka(dynamoClient)

  private val table: Table[User] = Table[User](tableName)

  override def addUser(user: User): Future[Option[Either[DynamoReadError, User]]] =
    scanamoAlpakka.execFuture(table.put(user))

  override def getUser(email: String): Future[Option[Either[DynamoReadError, User]]] =
    scanamoAlpakka.execFuture(table.get("email" -> email))

  override def updateUserName(email: String, updatedName:String): Future[Either[DynamoReadError, User]] =
    scanamoAlpakka.execFuture(table.update("email" -> email, set("name" -> updatedName)))

  override def deleteUser(email: String): Future[String] =
    scanamoAlpakka.execFuture(table.delete("email" -> email)).map(_.toString)
}

5. Initialise ActorSystem and DynamoClient: –

Initialize ActorSystem, ActorMaterializer, ExecutionContext and DynamoClient

implicit val system: ActorSystem = ActorSystem("Scanamo-tutorial")
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val executionContext: ExecutionContext = system.dispatcher

val dynamoSettings: DynamoSettings = DynamoSettings(system)
implicit val dynamoClient: DynamoClient = DynamoClient(dynamoSettings)

6. Insert some data: –

Its time to run our code by populating it with some data.

val tableName = "TestTable"
val userDAO: UserDAO = new DynamoDBImplementation(tableName)

val sampleUser = User(100L, "Test", "test@test@test")

userDAO.addUser(sampleUser).onComplete {
  case Success(Some(Right(user))) => println(s"$user ----- updated")
  case Success(Some(Left(dynamoReadError))) => println(dynamoReadError)
  case Success(None) => println("User inserted")
  case Failure(exception) => exception.printStackTrace()
}

7. Query inserted data: –

If data is inserted successfully, then we are going to get User using email from DynamoDB

userDAO.getUser(sampleUser.email).onComplete {
  case Success(Some(Right(user))) => println(s"$user")
  case Success(Some(Left(dynamoReadError))) => println(dynamoReadError)
  case Success(None) => println("User not found")
  case Failure(exception) => exception.printStackTrace()
}

So, from above we can execute async database operations on DynamoDB using scanamo library.

To setup local DynamoDB, Click Here.