Akka · REST · Spray · Tutorial

How to build a REST api with Spray and Akka

Spray is a library written on top of Akka and Scala that allows to quickly create REST interfaces. It is becoming more and more popular in the Scala community because it is easy to use and performant thanks to this asynchronous, actor-based model. This article describes how to efficiently exploit Spray to create a simple REST api.

All the code produced in this tutorial, together with a quick guide on how to run and use the application, can be found here.

Update: I wrote some follow ups on this article:
1) In this article, I used the default Spray facilities for (de)serializing objects from and to JSON. In this follow-up article, I’m showing how to make (de)serialization in Spray simpler by using the json4s library.
2) Also, this article doesn’t always respect the REST protocol or the spray way of completing an endpoint. In the following article, I explain how to build a REST CRUD application in Spray, making full advantage of the Spray’s tool kit.

Our Goal

For this tutorial we want to build a REST interface to manage quizzes. In particular we would like our interface to do the following:
– Create a quiz
– Delete a quiz
– Get a random question
– Get a question by id
– Answer a question

In the following sections we will analyse how to achieve these requirements. In particular, we will focus on how to create and delete a quiz entity. The described pattern can be re-used to achieve all the other requirements: describing all the requirements one by one would not bring any extra value to the tutorial itself. Please, have a look at the GitHub repository for the complete code of this tutorial and examples on how to use the application.

Project Metadata

First we need to set up our project. Let’s add some metadata: its name and version, its organization, what scala and SBT versions we intend to use.

// file build.sbt
name := "quiz-management-service"

version := "0.1"

organization := "com.danielasfregola"

scalaVersion := "2.11.5"
// file project/build.properties
sbt.version=0.13.6

Plugins

Next step: make our life a lot easier! By importing the following plugins, we just need to run the sbt assembly command to assembly our code: it will compile the code, run all the tests and produce an executable java jar.

// file project/plugins.sbt
resolvers += Classpaths.typesafeResolver

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0-M4")

addSbtPlugin("com.typesafe.sbt" % "sbt-multi-jvm" % "0.3.9")
// file build.sbt
enablePlugins(JavaServerAppPackaging)
...

// Assembly settings
mainClass in Global := Some("com.danielasfregola.quiz.management.Main")

jarName in assembly := "quiz-management-server.jar"

To compile, test and assembly the code:

cd quiz-management-service/spray-akka
sbt assembly

The produced jar will be called “quiz-management-server.jar” and it will be use the main class located in the package “com.danielasfregola.quiz.management”. To run our service (by default on http://localhost:5000), we just need to type the following command:

java -jar target/scala-2.11/quiz-management-service.jar

Spray Dependencies

Almost there with the boring part! We now have to include the spray libraries that we are going to use:

// file build.sbt
resolvers ++= Seq("Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/",
"Spray Repository" at "http://repo.spray.io")

libraryDependencies ++= {
    val akkaVersion = "2.3.9"
    val sprayVersion = "1.3.2"
    Seq(
        "com.typesafe.akka" %% "akka-actor" % akkaVersion,
        "io.spray" %% "spray-can" % sprayVersion,
        "io.spray" %% "spray-routing" % sprayVersion,
        "io.spray" %% "spray-json" % "1.3.1",
        "com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
        "ch.qos.logback" % "logback-classic" % "1.1.2",
        "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
        "io.spray" %% "spray-testkit" % sprayVersion % "test",
        "org.specs2" %% "specs2" % "2.3.13" % "test"
    )
}

Main Class

The first thing to do in our Main class is to load our system configurations. Then, we create our actor system called “quiz-management-service” that contains our http interface — remember that Spray is actor-based! Finally, our actor system is bound to an HTTP port.

package com.danielasfregola.quiz.management

import akka.actor._
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import com.typesafe.config.ConfigFactory
import spray.can.Http

import scala.concurrent.duration._

object Main extends App {
    val config = ConfigFactory.load()
    val host = config.getString("http.host")
    val port = config.getInt("http.port")
    
    implicit val system = ActorSystem("quiz-management-service")
    
    val api = system.actorOf(Props(new RestInterface()), "httpInterface")
    
    implicit val executionContext = system.dispatcher
    implicit val timeout = Timeout(10 seconds)
    
    IO(Http).ask(Http.Bind(listener = api, interface = host, port = port))
    .mapTo[Http.Event]
    .map {
        case Http.Bound(address) =>
            println(s"REST interface bound to $address")
        case Http.CommandFailed(cmd) =>
            println("REST interface could not bind to " +
            s"$host:$port, ${cmd.failureMessage}")
        system.shutdown()
    }
}

Rest Interface Class

The Rest interface class is an HttpServiceActor: every time it receives an HTTP request, it tries to match it to a routing defined in our RestApi trait. When a matching is found, the action defined is executed and the result sent to a Responder actor. The Responder actor is responsible for sending back a meaningful HTTP response with an appropriate status code and body. Every time we receive a valid HTTP request, we create an instance of the Responder actor and then kill it once the request has been completed.

package com.danielasfregola.quiz.management

import akka.actor._
import akka.util.Timeout
import spray.http.StatusCodes
import spray.httpx.SprayJsonSupport._
import spray.routing._

import scala.concurrent.duration._
import scala.language.postfixOps

class RestInterface extends HttpServiceActor
with RestApi {

    def receive = runRoute(routes)
}

trait RestApi extends HttpService with ActorLogging { actor: Actor =>

    implicit val timeout = Timeout(10 seconds)
    
    var quizzes = Vector[Quiz]()
    
    def routes: Route = ???
}

class Responder(requestContext:RequestContext) extends Actor with ActorLogging {

    def receive = ???
    
    private def killYourself = self ! PoisonPill

}

For the purposes of this tutorial, we will store all the data in a vector: this is not an ideal solution for a production system, but it allows this article to focus on how Spray works. In a more realistic context, we should delegate actions to other classes — or even better actors! — and store the data in a persistent data storage (e.g.: database).

How to create and delete a quiz

Our interface needs to provide the functionalities of creating and deleting a quiz entity. This can be translated in the following REST calls:
– POST to URI /quizzes with JSON body: {"id": "my_quiz_id", "question": "my_question", "correctAnswer": "my_answer"}
– DELETE to URI /quizzes/my_quiz_id with no body

First, we need to define what a Quiz is and the messages that we can send to the Responder actor. This can easily be achieved by using case classes and objects:

// file src/main/scala/com/danielasfregola/quiz/management/QuizProtocol.scala
...
// entity: quiz
case class Quiz(id: String, question: String, correctAnswer: String)

// message: quiz has been created
case object QuizCreated

// message: quiz cannot be created because it already exists
case object QuizAlreadyExists

// message: quiz has been deleted
case object QuizDeleted
...

Also, we need instructions on how to (un)marshall a Quiz case class.

// file src/main/scala/com/danielasfregola/quiz/management/QuizProtocol.scala
...
object Quiz extends DefaultJsonProtocol {
    implicit val format = jsonFormat3(Quiz.apply)
}
...

The RestApi trait defines the routing and the operation to execute. For each matched HTTP request we create an instance of the Responder actor: once the result of the executed operation is available we send it to the Responder actor.

....

var quizzes = Vector[Quiz]()

def routes: Route =

pathPrefix("quizzes") {
    pathEnd {
        post {
            entity(as[Quiz]) { quiz => requestContext =>
                val responder = createResponder(requestContext)
                createQuiz(quiz) match {
                    case true => responder ! QuizCreated
                    case _ => responder ! QuizAlreadyExists
                }
            }
        }
    } ~
    path(Segment) { id =>
        delete { requestContext =>
            val responder = createResponder(requestContext)
            deleteQuiz(id)
            responder ! QuizDeleted
        }
    }
}
...

private def createQuiz(quiz: Quiz): Boolean = {
    val doesNotExist = !quizzes.exists(_.id == quiz.id)
    if (doesNotExist) quizzes = quizzes :+ quiz
    doesNotExist
}

private def deleteQuiz(id: String): Unit = {
    quizzes = quizzes.filterNot(_.id == id)
}

private def createResponder(requestContext:RequestContext) = {
    context.actorOf(Props(new Responder(requestContext)))
}

When the Responder actor receives a message it maps it to a meaningful HTTP response: it sends it back to the HTTP requester and it kills itself ( 😥 ).

// file src/main/scala/com/danielasfregola/quiz/management/RestInterface.scala
class Responder(requestContext:RequestContext) extends Actor with ActorLogging {
import com.danielasfregola.quiz.management.QuizProtocol._

def receive = {

case QuizCreated =>
    requestContext.complete(StatusCodes.Created)
    killYourself

case QuizDeleted =>
    requestContext.complete(StatusCodes.OK)
    killYourself

case QuizAlreadyExists =>
    requestContext.complete(StatusCodes.Conflict)
    killYourself

....
}

private def killYourself = self ! PoisonPill

Summary

This article has described how to use Spray to create a REST interface. Spray is a library that uses a actor-based model to easily and efficiently implement a REST layer. Although the provided example in not applicable in a realistic system (because all the data is stored in a Vector variable!), it shows a simple pattern on how to structure a REST api using Spray: how to organise the communication between actors, the routing and the response handling.

7 thoughts on “How to build a REST api with Spray and Akka

Leave a comment