Skip to content

26lights/PlayR

Repository files navigation

Play'R

Play'R stands for: Playing with Play and ReST.

It's a simple, uniform, and introspectable way to declare ReST APIs in play.

simple

Just implements some traits and Play'R will do the routing for you.

uniform

Play'R defines an uniform mapping from http methods to controller methods.

introspectable
the declared resources capabilities can be listed and used to generate documentation or ui connectors.
See the PlayR-swagger project as an example.

Development is supported by 26lights.

Warning

Play'R is a young project and the API is subject to change.

Features

  • Uses standard Play controllers
  • Map HTTP verbs to controller methods via traits implementation.
  • Resource routing and dependencies defined in Scala
  • Introspection API allowing you to generate content from your defined ReST API

Usage

To use Play'R in your sbt based project, you should add a resolver for the 26lights public repository:

resolvers += "26Lights releases" at "http://build.26source.org/nexus/content/repositories/public-releases"

and add Play'R to your library dependencies:

libraryDependencies ++= Seq (
  "26lights"  %% "playr"  % "0.4.0"
)

Quick Example

A working version of this example is located in the samples/playr-demo project.

Just start it with sbt run and try it with curl or other HTTP tools.

Source code

Let's start with a simple read only resource that manages a list of person

import play.api.mvc._
import play.api.libs.json.Json
import twentysix.playr._
import twentysix.playr.simple._


case class SimplePerson(name: String)

object SimplePersonController extends Controller
                                 with Resource[Person]
                                 with ResourceRead {
  def name = "person"

  def persons = Map(
    1 -> SimplePerson("john"),
    2 -> SimplePerson("jane")
  )

  implicit val personFormat = Json.format[SimplePerson]

  def fromId(sid: String): Option[SimplePerson] = toInt(sid).flatMap(persons.get(_))

  def read(person: SimplePerson) = Action { Ok(Json.toJson(person)) }

  def list() = Action { Ok(Json.toJson(persons.keys)) }
}

object SimplePersonRouter extends RestResourceRouter(SimplePersonController)

First, we define a case class that represents a person.

case class SimplePerson(name: String)

Next, we define a Play controller that implements two Play'R traits.

object SimplePersonController extends Controller
                                 with Resource[Person]
                                 with ResourceRead

The Resource trait extends Controller, defines basic resource capabilities and it requires you to define:

name

a name that can be used by the router to access your resource

fromId

a method to retrieve your resource instance from an identifier (given in the URL)

The ResourceRead trait defines that there is a way to read that resource; it requires you to define two methods:

list

respond to an http GET method on the resource's path, in this case, it returns the list of available id

read

respond to an http GET method on an identified resource, in this case, it returns the person object serialized as JSON.

Finally, we define a RestResourceRouter instance that will route incoming requests for that resource.

object SimplePersonRouter extends RestResourceRouter(SimplePersonController)

The only missing step is to reference this router in Play's routes file.

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

->      /person                     controllers.SimplePersonRouter

Demo

To show how the router works, let's use curl with some url.

$ curl -f http://localhost:9000/person
[1,2]

A simple http GET on the person resource returns the list of available ids as a json list. It's the result of the controller's list method.

$ curl -f http://localhost:9000/person/1
{"name":"john"}

If we add a valid id to the URL, we get the JSON version of that resource. It's the result of the controller's read method.

Let's try to find what methods our resource support:

$ curl -f -XOPTIONS -i http://localhost:9000/person
HTTP/1.1 200 OK
Allow: GET
Content-Length: 0

Let's try some erroneous requests.

First, a not supported method on the resource:

$ curl -f -XPOST http://localhost:9000/person
curl: (22) The requested URL returned error: 405 Method Not Allowed
$ curl -f -XPOST http://localhost:9000/person/1
curl: (22) The requested URL returned error: 405 Method Not Allowed

Returns the expected «method not supported» code, both for the resource itself and the identified resource.

$ curl -f http://localhost:9000/person/5
curl: (22) The requested URL returned error: 404 Not Found

There are only two existing person resource, id 5 is invalid, so it returns «not found»

More documentation

A more complete documentation, showing all supported HTTP methods, and more complex routing with sub-resources is available in the Play'R documentation

The associated code is in the samples/playr-tutorial project.

TODO

Play'R can already be used to develop ReST API, but it's only a starting point and a lot more is left to do, like:

  • Use objects for HTTP verbs instead of strings
  • Multiple HTTP method per action
  • Routing configuration DSL
  • Reverse routing
  • Resource type introspection
  • Transactional router