Play Framework async controller method examples

If you want to write “async” Play Framework controller methods, I hope the following example code will help you get started. I’ll offer it here without much description today, other than to say that the list and add methods are written to work asynchronously by invoking Action.async and using a Scala Future:

package controllers

import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.Constraints._
import views._
import models._
import scala.collection.mutable.ArrayBuffer
import play.api.libs.json._
import play.api.libs.json.Json
import play.api.libs.json.Json._
import java.util.Calendar

object Transactions extends Controller {
   
  val transactionForm: Form[Transaction] = Form(
    // the names you use in this mapping (such as 'symbol') must match the names that will be
    // POSTed to your methods in JSON.
    // note: skipping `id` field
    // note: skipping `datetime` field
    mapping(
      // verifying here creates a field-level error; if your test returns false, the error is shown
      "symbol" -> nonEmptyText,
      "ttype" -> nonEmptyText,
      "price" -> bigDecimal,
      "quantity" -> number,
      "notes" -> text
      )
      // transactionForm -> Transaction
      ((symbol, ttype, price, quantity, notes) => Transaction(0, symbol, ttype, price, quantity, Calendar.getInstance.getTime, notes))
      // Transaction -> TransactionForm
      ((t: Transaction) => Some(t.symbol, t.ttype, t.price.toDouble, t.quantity, t.notes))
)
 
  // needed to return async results
  import play.api.libs.concurrent.Execution.Implicits.defaultContext
 
  /**
   * Create a list of transactions, asynchronously.
   */
  def list = Action.async {
    val transactionsAsFuture = scala.concurrent.Future{ Transaction.getAll }
    transactionsAsFuture.map(transactions => Ok(Json.toJson(transactions)))
  }

  /**
   * Delete a transaction, asynchronously.
   */
  def delete(id: Long) = Action.async {
    val futureNumRowsDeleted = scala.concurrent.Future{ Transaction.delete(id) }
    // TODO handle the case where 'count < 1' properly
    futureNumRowsDeleted.map{ count =>
        val result = Map("success" -> toJson(true), "msg" -> toJson("Transaction was deleted"), "id" -> toJson(count))
        Ok(Json.toJson(result))
    }
  } 

  /**
   * The Sencha client will send me id, symbol, and companyName in a POST request.
   * I need to return something like this on success:
   *     { "success" : true, "msg" : "", "id" : 100 }
   */
  def add = Action { implicit request =>
    transactionForm.bindFromRequest.fold(
      errors => {
        Ok(Json.toJson(Map("success" -> toJson(false), "msg" -> toJson("Boom!"), "id" -> toJson(0))))
      },
      transaction => {
        val id = Transaction.insert(transaction)
        id match {
          case Some(autoIncrementId) =>
              val result = Map("success" -> toJson(true), "msg" -> toJson("Success!"), "id" -> toJson(autoIncrementId))
              Ok(Json.toJson(result))
          case None =>
              // TODO inserts can fail; i need to handle this properly.
              val result = Map("success" -> toJson(true), "msg" -> toJson("Success!"), "id" -> toJson(-1))
              Ok(Json.toJson(result))
        }
       
      }
    )
  }

}

For more Play Framework async examples, see this link: playframework.com/documentation/2.2.x/ScalaAsync.