Building a REST API in Java and Scala Using Play Framework – Part 2 Vassili van der Mersch February 8, 2016 In the first part of this series, we introduced Play framework, a web development framework for Java and Scala developers, and we showed how it enabled one to expose a basic REST API with minimal effort. In this second part, we explore some of the features of Play and use real code to illustrate its capabilities. Asynchronous Actions Play supports both synchronous and asynchronous REST services, and it’s particularly powerful when dealing with asynchronous requests. Whatever the request, an Action always returns an instance of play.mvc.Result. This can be directly returned to the consumer in the case of a synchronous service, or it can be served up as a Future. In this case it will produce a response only when the desired function has finished computing. This comes in very handy when the result depends on an external API call or a long computation. In the meantime, the thread that was executing the Controller’s code is freed up to take care of something else – the rest of the code will be handled when the computation thread has finished working. This is what is called non-blocking I/O. In the example below, we want to calculate the score for a list of players of a game. There could be a lot of players, and since the score calculation function is relatively computation-intensive, we don’t know how long the full computation will last. We want to return a JSON object with the scores once we’ve collected them all. [1] def getPlayerScores(ids: List[String]) = Action.async { implicit request => val futureScores = for(id <- ids) yield calculatePlayerScore(id) futureScores map { scores => Ok(Json.obj("scores" -> scores.mkString(";"))) } } In the first part of the getPlayerScores action above, the ‘for comprehension’ returns a Future, which is a handle on the eventual value of a long computation (in this case, calculating the scores of each player one by one). The second part instructs the controller to wait for all the scores to be computed and return the resulting values within a serialized JSON object. Note that as this Action returns a Future, we need to specify the return type as Action.async. Learn how to develop Description Agnostic APIs with API Transformer Asynchronous Web Services Play also features a powerful WebServices (WS) library that enables one to make external calls of its own within an Action. All WS functions are asynchronous and non-blocking. [2] def getDistance = Action.async { implicit request => val origin: String = request.getQueryString.get("origin") val destination: String = request.getQueryString.get("destination") val futureDistance = WS.url(apiUrl + "?origins=" + origin + "&destinations=" + destination + "&key=" + apiKey).get futureDistance map { response => val distanceText: String = parseGoogleJson(response.json) Ok(Json.obj("distance" -> distanceText)) } } [3] Map this action to an HTTP endpoint. It accepts two URL parameters — ‘origin’ and ‘destination’. If we pass URL encoded versions of ‘Stockholm+Sweden’ to the origin parameter and ‘Brussels+Belgium’ to the destination parameter, we’ll retrieve the distance between these two locations from the Google Distance Matrix API, and serve the value of the distance back to our client within a JSON String. Future Composition The previous example wasn’t very useful because we just created a proxy for the Google API. Let’s try to make something more interesting by combining several outbound API calls within a single Action. Suppose we have some solid marketing copy, like the keyword-rich contents of a website’s landing page. We want to find relevant Internet domain names that could be a good match for this blurb of text. We’re going to combine two APIs from the Mashape API marketplace (TextAnalysis and Blazing Domain Availability Search) using Play’s Web Services library and Scala’s Future composition (the same can be achieved with Promises in Java). We’ll start by writing two private functions that will each deal with one of the API calls. val mashapeApiKey = "FbkOCUKf8hMopHO5I8u2pGBUli2Dp14ZSMIjsnh0ky1T5tmpK1" private def getNounPhrases(text: String): Future[List[String]] = { val textAnalysisUrl = "https://textanalysis.p.mashape.com/textblob-noun-phrase-extraction" val futureKeyPhrases = WS.url(textAnalysisUrl) .withHeaders("X-Mashape-Key" -> mashapeApiKey, "Content-Type" -> "application/x-www-form-urlencoded", "Accept" -> "application/json") .post(Map("text" -> Seq(text)) ) futureKeyPhrases map { keyPhrases => (keyPhrases.json \ "noun_phrases").as[List[String]].map(phrase => phrase.replace(" ", "")) } } The first function calls the TextAnalysis API and posts the text contents we want to analyze. The Key Phrase Extractor endpoint parses the text and extracts useful noun phrases. To test them for domain name availability, we remove blank spaces from each of these String values. Note that the result of this function is a Future List of Strings. private def getAvailableDomains(phrase: String): Future[List[String]] = { val domainSearchUrl = "https://domainsearch.p.mashape.com/index.php?name=" val futureDomains = WS.url(domainSearchUrl + phrase) .withHeaders("X-Mashape-Key" -> mashapeApiKey, "Accept" -> "application/json").get futureDomains map { domainNames => val domains: Map[String, String] = domainNames.json.as[Map[String, String]] domains.collect { case (name, status) if status == "Available" => name } } } The second function takes a given phrase and calls the Blazing Domain Availability Search. This will test several domain names related to the phrase (.com, .net, .biz, etc) for availability and return its findings. We’ll keep only the available domain names. Once again, we get a Future List of Strings. We need to be able to call this last function for a list of noun phrases, so we’ll make a handy wrapper function that accepts an argument of type List[String]: private def getAvailableDomains(phrases: List[String]): Future[List[String]] = { Future.sequence(phrases.flatMap(phrase => getAvailableDomains(phrase))) } We now have all the basic ingredients — we just need to aggregate the results in a Play Action. def getDomainNames(text: String) = Action.async { implicit request => for { phrases <- getNounPhrases(text) domains <- getAvailableDomains(phrases) } yield { Ok(Json.obj("available_domains" -> domains.mkString(";"))) } } Scala’s for comprehensions come in handy here — the code above calls the getNounPhrases and getAvailableDomains functions in succession, using the result of the first function as an argument to the second (underneath the covers, a flatMap and a map function have been called), resulting in a list of domain names that we’re serving back as JSON. The example above illustrates how Play Framework and Scala enable a tight definition of relatively complex processing. We’ve built something quite useful in just a few lines of code. Streaming API Play enables multiple ways of streaming data. One option is to open a WebSocket with the server, but since we’re trying to keep our API RESTful, we’ll stick with Actions in the below example and use chunked transfer encoding. [4] def videoStream = Action { val stream = new FileInputStream("/radioactive.mp4") val streamContent: Enumerator[Array[Byte]] = Enumerator.fromStream(stream) Ok.chunked(streamContent) } In the example above we’ve used Play’s Enumerator type to cut an .mp4 file down into chunks (byte arrays) and feed them to the client using a chunked response (note that you could also serve the file back using Ok.sendFile). Persistence The latest versions of Play framework no longer come with a persistence framework out of the box. Play has a long time association with drivers for SQL databases Anorm and Slick, but either of these need to be imported as separate modules in SBT. The purpose is to keep the core framework as lightweight as possible. In order to achieve a fully asynchronous and non-blocking layer with Play, some developers forego the inclusion of data persistence code within Play framework. Instead, they delegate this responsibility to a backend such as Akka or Finagle. To better fit with Play’s non-blocking I/O philosophy, however, it’s best to couple it with a database that features an asynchronous, non-blocking driver. This is the case of MongoDB. Play’s own team was involved in the creation of ReactiveMongo, an asynchronous driver for MongoDB. When using Play and ReactiveMongo, the developer can opt for the JSON ‘coast-to-coast’ design. In this case, no model classes need to be defined as the JSON that is used by the client and processed through Play is directly stored as BSON files in MongoDB (with potential validation and transformation steps in between). Implement Content Negotiation for Web API Longevity Tips and Tricks Play’s learning curve isn’t very steep if you already know Java or Scala, but it takes a bit longer to truly master its ins and outs. Here are some quick pointers on how to solve common problems and enrich your Play-powered REST API. 1. Action composition You can define custom Actions and common behaviour across several Actions using ActionBuilder. This works by creating a wrapper around the standard Action class and enables you to add logging or authentication to certain requests, or to modify the Result (e.g. by adding an HTTP header). 2. Recovering from exceptions In an earlier section we saw how useful Play’s Web Services library could be. In many cases when you’re calling external APIs and other resources, you’re going to have to handle a host of errors and timeouts. The WS library enables you to manage timeouts easily: WS.url(“https://api.twitter.com/1.1/search/tweets.json?q=%40play+framework”) .withRequestTimeout(5000) .get() Scala lets you recover gracefully from exceptions in Futures: futureTweets recover { case Exception => “no tweets could be found” } 3. Traits (or Custom input validation) Using Scala’s traits, you can mix in custom functions into your Actions. For Java developers, traits are similar to interfaces but with implementations that you can include in classes and objects that feature this trait. In the example below, we’ve mixed in a validator trait into our Controller to do basic input validation on incoming requests. object Application extends Controller with BasicValidator { def getPicture(name: String) = Action { if(BasicValidator.validate(name)) Ok("here's your pic: " + name) else Ok("invalid picture name") } 4. CSRF protection While we’re on the topic of input validation and security, Play provides CSRF protection in the form of a global filter that can be applied to all requests. The filter generates a new CSRF token for all GET requests – the token is then available in any form that might be submitted in the following request. Footnotes: [1] to use Futures in your Play Controller, you’ll need the following import statements at the top of your Controller file: import play.api.libs.concurrent.Execution.Implicits._ import scala.concurrent.Future import play.api.Play.current [2] To use Play’s Web Services library, you need to include these two import statements: import play.api.libs.ws.WS import play.api.libs.ws.WSResponse [3] Here’s the parseGoogleJson function we’ve used to retrieve the distance from Google’s Distance Matrix API: private def parseGoogleJson(json: JsValue): String = { val rows = (json \ "rows").as[List[JsObject]] val jsonDistance = (rows.head \ "elements").as[List[JsObject]].head (jsonDistance \ "distance" \ "text").asOpt[String].get } [4] The streaming example requires two more import statements: import play.api.libs.iteratee.Enumerator import java.io.FileInputStream EDIT: Thanks to Julien Lafont for your suggestions to make this project’s code more idiomatic & concise. The latest API insights straight to your inbox