Scala

Building a rest api with spray

Building a rest api with akka and spray is easy. This is how I did it:
SprayApiApp:
 
 
 
 
 
 
 
 
 

import akka.actor.{ActorSystem, Props}
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import spray.can.Http
import scala.concurrent.duration._

object SprayApiApp extends App {
  //we need an ActorSystem to host our application in
  implicit val system = ActorSystem("SprayApiApp")

  //create apiActor
  val apiActor = system.actorOf(Props[ApiActor], "apiActor")

  //timeout needs to be set as an implicit val for the ask method (?)
  implicit val timeout = Timeout(5.seconds)

  //start a new HTTP server on port 8080 with apiActor as the handler
  IO(Http) ? Http.Bind(apiActor, interface = "localhost", port = 8080)
}

ApiActor:

import akka.actor.{ActorLogging, Actor}
import spray.http.MediaTypes
import spray.httpx.SprayJsonSupport._
import spray.json.DefaultJsonProtocol
import spray.routing._

object RobotProtocol extends DefaultJsonProtocol {
  //Our domain class
  case class Robot(name: String)

  //We use the default json marshalling for Robot.
  //There are multiple jsonFormat methods in DefaultJsonProtocol. Depending on how many parameters the model class has.
  //Robot has just one, so we use jsonFormat1
  implicit val RobotFormat = jsonFormat1(Robot)
}
import RobotProtocol._

class ApiActor extends Actor with HttpService with ActorLogging {
  //A list of our domain objects
  var robots = List(Robot("R2D2"), Robot("Asimo"))

  //The HttpService trait defines only one abstract member, which
  //connects the services environment to the enclosing actor or test
  def actorRefFactory = context

  //This actor only runs our route, but you could add
  //other things here, like request stream processing or timeout handling
  def receive = runRoute(apiRoute)

  //Notice that both path methods return a Route. We need to chain them together with ~
  val apiRoute: Route =
    path("robots") {
      get { //with get we will return our current list of robots
        log.info("Building get route")
        complete {
          log.info("Executing get route")
          //complete will return the result in an appropriate format
          //With SprayJsonSupport it knows how to marshall a List to json
          //With RobotFormat it knows how to marshall Robot
          robots
        }
      } ~ post { //With post we will add a robot
        log.info("Building post route")
        handleWith { robot: Robot =>  //handleWith will unmarshall the input
          log.info("Executing post route")
          robots = robot :: robots
          robot //handleWith will also marshall the result. Here we simply return the new robot.
        }
      }
    } ~ path("") { //When we go to localhost:8080/ just show a link to localhost:8080/robots
      respondWithMediaType(MediaTypes.`text/html`) { //XML is marshalled to `text/xml` by default, so we simply override here
        complete {
          <a href="/robots">The list of robots</a>
        }
      }
    }
}

When we run this, we can do a get on http://localhost:8080/robots and get the list of robots:

[{
  "name": "R2D2"
}, {
  "name": "Asimo"
}]

And we can do a post to http://localhost:8080/robots to add a robot: with header Content-Type: application/json

{
  "name": "Optimus Prime"
}

When we take a look at the logging, we notice that the code in the route construction will only run once during startup.

Only the code within complete and handleWith will be run with every request.

[INFO] [03/15/2015 12:08:21.572] [SprayApiApp-akka.actor.default-dispatcher-3] [akka://SprayApiApp/user/apiActor] Building get route
[INFO] [03/15/2015 12:08:21.578] [SprayApiApp-akka.actor.default-dispatcher-3] [akka://SprayApiApp/user/apiActor] Building post route
[INFO] [03/15/2015 12:08:22.144] [SprayApiApp-akka.actor.default-dispatcher-4] [akka://SprayApiApp/user/IO-HTTP/listener-0] Bound to localhost/127.0.0.1:8080
[INFO] [03/15/2015 12:08:27.243] [SprayApiApp-akka.actor.default-dispatcher-3] [akka://SprayApiApp/user/apiActor] Executing get route
[INFO] [03/15/2015 12:08:30.066] [SprayApiApp-akka.actor.default-dispatcher-4] [akka://SprayApiApp/user/apiActor] Executing post route

with thanks to: http://blog.michaelhamrah.com/2013/06/scala-web-apis-up-and-running-with-spray-and-akka/

Reference: Building a rest api with spray from our JCG partner Tammo Sminia at the JDriven blog.

Arthur Arts

Arthur Arts is an experienced Enterprise Java software engineer and hands-on Agile Coach and Scrum Master. He has a strong focus on agile engineering principles and practices and loves to share his knowledge and learn from others. He has his own company Coded Value, is a passionate photographer and writes for agilearts.nl.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button