Scala best practices: Simple JSON API with Play

As a PHP developer, it’s quite hard to transform into Scala for me. Scala syntaxes, styles look very strange.. It’s costs me a day to handle parameters from html form to server.

“It is the first step that costs” – they said 🙂

I think the best way to practice Scala is in examples.

scala-api

then in this article, you can knowing about:
– Handle form submission
– Working with JSON library
– Relational database access with Scala

Our stragegy is create an scala application with Play framework, that has 2 endpoints of API

  1. /getall : Return all data in table customers
  2. /create: Acquires parameters then insert new user to database.

All API return a JSON object whith is the most common data format nowaday.

1. Database Preparation

We need to create a simple table, which contain our customer’s informations:

CREATE TABLE `customers` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) DEFAULT NULL,
 `age` int(11) DEFAULT NULL,
 `email` varchar(255) DEFAULT NULL,
 `role` enum('ADMIN','USER','GUEST') DEFAULT 'GUEST',
 PRIMARY KEY (`id`)
)

2. Routing

To handle 2 endpoints as above, we need to defined 2 routes in config/routes file:

POST        /create         controllers.Application.createCustomer
GET         /getall              controllers.Application.getAll

The first route to handle post data then insert it to database, and the second is just list all our records without parameters

3. Coding

Now we have to define our method in Application.scala as below:

Before doing thing, we need to import some needed packages :

import anorm._
import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import models.CustomerData
import play.api.libs.json._

def createCustomer = Action { implicit request =˃
  customerForm.bindFromRequest().fold(
    formWithErrors =˃ {
      Ok(Json.toJson(Json.obj(
        "error" -˃ formWithErrors.errorsAsJson
      )))
    },
    customer =˃ {
      val cusId = CustomerData.create(customer.name, customer.age, customer.email, customer.role)
      Ok(Json.toJson(Json.obj("id" -˃ cusId.toString)))
    }
  )
}

In this method, we need to handle the submitted data including the binding error with fold operator (more detail here).

If parameter is corrected, we pass them to create method of CustomerData model. The create function require 4 parameters as Customer’s information then return the last insert ID as customer’s ID.

Now we’re going to create the CustomerData model:

package models
import anorm._
import anorm.SqlParser._
import java.sql.SQLException
import play.api.db.DB
import play.api.Play.current

def create(name: String, age: Int, email: String, role: String): Any = {
  try {
    DB.withConnection { implicit c =˃
      val cusId: Option[Long] = SQL(
        """
          INSERT INTO customers (name, age, email, role)
          VALUES
          ({name}, {age}, {email}, {role})
        """).on("name" -˃ name, "age" -˃ age, "email" -˃ email, "role" -˃ role).executeInsert()

      cusId.get
    }
  }
  catch {
    case ex: Exception =˃ ex.getMessage
  }
}

This method simply call the executeInsert of java.sql package to insert data to table then return last insert ID with cusId.get

Now let use and Chrome extension called POSTMan to make post request to our API:

create

 

The customer has created successfully with ID returned

Great! It’s works ! 😀

Next, we’re going to build the /getall endpoint to list all the the records in customers table

In Application.scala

def getAll = Action {
  implicit val customerWrites = new Writes[CustomerData] {
    def writes(cusData: CustomerData) = Json.obj(
      "id" -˃ cusData.id.toString,
      "name" -˃ cusData.name,
      "email" -˃ cusData.email,
      "age" -˃ cusData.age,
      "role" -˃ cusData.role
    )
  }
  Ok(Json.toJson(Json.obj("data" -˃ Json.toJson(CustomerData.getAll))))
}

Because our model will return the record as an object model, then we need an Writer to parse it to JSON format

In CustomerData.scala

case class CustomerData(id: Pk[Long], name: String, email: String, age: Int, role: String)

...
val customerMapping = {
  get[Pk[Long]]("id") ~
  get[String]("name") ~
  get[String]("email") ~
  get[Int]("age") ~
  get[String]("role") map {
    case id ~ name ~ email ~ age ~ role =˃ CustomerData(id, name, email, age, role)
  }
}
def getAll: Seq[CustomerData] = {
  DB.withConnection {implicit c =˃
    SQL("SELECT * FROM customers").as(customerMapping *)
  }
}

The above code block simply get all the records then map them with CustomerData case class

Let check how it works

getall

Work like a charm 😉

That’s enough for today, if you want to download the source code in this article, please use this link

Add a Comment

Scroll Up