How to write a GET request web service with the Play Framework

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 15.14, “How to write a GET request web service with the Scala Play Framework.”

Problem

You want to create a GET request web service using the Play Framework, such as returning a JSON string when the web service URI is accessed.

Solution

When working with RESTful web services, you’ll typically be converting between one or more model objects and their JSON representation.

To demonstrate how a GET request might be used to return the JSON representation of an object, create a new Play project with the play new command:

$ play new WebServiceDemo

Respond to the prompts to create a new Scala application, and then move into the WebServiceDemo directory that’s created.

Next, assume that you want to create a web service to return an instance of a Stock when a client makes a GET request at the /getStock URI. To do this, first add this line to your conf/routes file:

GET   /getStock     controllers.Application.getStock

Next, create a method named getStock in the default Application controller (apps/controllers/Application.scala), and have it return a JSON representation of a Stock object:

package controllers

import play.api._
import play.api.mvc._
import play.api.libs.json._
import models.Stock

object Application extends Controller {
    def index = Action {
        Ok(views.html.index("Your new application is ready."))
    }
    def getStock = Action {
        val stock = Stock("GOOG", 650.0)
        Ok(Json.toJson(stock))
    }
}

That code uses the Play Json.toJson method. Although the code looks like you can create Stock as a simple case class, attempting to use only a case class will result in this error when you access the /getStock URI:

No Json deserializer found for type models.Stock. 
Try to implement an implicit Writes or Format for this type.

To get this controller code to work, you need to create an instance of a Format object to convert between the Stock model object and its JSON representation. To do this, create a model file named Stock.scala in the app/models directory of your project. (Create the directory if it doesn’t exist.)

In that file, define the Stock case class, and then implement a play.api.libs.json.Format object. In that object, define a reads method to convert from a JSON string to a Stock object and a writes method to convert from a Stock object to a JSON string:

package models

case class Stock(symbol: String, price: Double)

object Stock {
    import play.api.libs.json._
    implicit object StockFormat extends Format[Stock] {

        // convert from JSON string to a Stock object (de-serializing from JSON)
        def reads(json: JsValue): JsResult[Stock] = {
            val symbol = (json \ "symbol").as[String]
            val price = (json \ "price").as[Double]
            JsSuccess(Stock(symbol, price))
        }

        // convert from Stock object to JSON (serializing to JSON)
        def writes(s: Stock): JsValue = {
            // JsObject requires Seq[(String, play.api.libs.json.JsValue)]
            val stockAsList = Seq("symbol" -> JsString(s.symbol),
                                  "price" -> JsNumber(s.price))
            JsObject(stockAsList)
        }
    }
}

The comments in that code help to explain how the reads and writes methods work.

With this code in place, you can now access the getStock web service. If you haven’t already done so, start the Play console from within the root directory of your project, then issue the run command:

$ play
[WebServiceDemo] $ run 8080

Play runs on port 9000 by default, but this collides with other services on my system, so I run it on port 8080, as shown. Assuming that you’re running on port 8080, access the http://localhost:8080/getStock URL from a web browser. You should see this result in the browser:

{"symbol":"GOOG","price":650.0}

Discussion

When converting from a Stock object to its JSON representation, the writes method of your Format object is implicitly used in this line of code:

Json.toJson(stock)

Although there are other approaches to converting between objects and their JSON representation, implementing the reads and writes methods of a Format object provides a straightforward means for this serialization and deserialization process.

See Also