Write a simple json REST server using spray in scala

jonderry picture jonderry · Jun 7, 2014 · Viewed 7k times · Source

I want to implement a simple json REST server using spray in scala that supports the following routes:

GET /foo => return a list of case class objects in json format
POST /bar => read a json into a case class object and perform some computation

My basic starter code is the following:

import spray.routing.SimpleRoutingApp
import spray.can.Http
import akka.actor.ActorSystem
import akka.actor.Props
import akka.io.IO
import scala.collection.JavaConversions
import com.fasterxml.jackson.databind.ObjectMapper

object SprayTest extends App with SimpleRoutingApp {
  implicit val system = ActorSystem("my-system")
  val mapper = new ObjectMapper

  case class Foo(a: String, b: Int)
  case class Bar(c: Long, d: String)

  startServer(interface = "localhost", port = 8080) {
    get {
      path("foo") {
        complete {
          val c = listOfFoo()
          mapper.writeValueAsString(c)
        }
      }
    } ~ post {
      path("bar") {
        val bar: Bar = ???
        complete {
          "???"
        }
      }
    }
  }
}

The two most important open issues with this code that I know of are:

  1. I'm depending on jackson, but from searching the web, it seems like spray should have some sort of built in support for serializing and deserializing simple case objects or lists of case objects.

  2. I'm not sure the "best", most idiomatic and succinct way to get content from the post request and marshall it into json so that I can perform some computations on the case class object(s)

Does anyone know the best approach? Is there a way to make the marshalling automatic, so I can execute something like complete { caseObject } and have caseObject convert automatically into json (and vice versa with defining POST method)?

Answer

Gangstead picture Gangstead · Jun 7, 2014

Definitely use spray json. Usually you separate the data models into their own file:

import spray.json._

case class Foo(a: String, b: Int)
case class Bar(c: Long, d: String)

object FooBarJsonProtocol extends DefaultJsonProtocol{
    implicit val fooFormat = jsonFormat2(Foo)
    implicit val barFormat = jsonFormat2(Bar)
}

Then in the route

    import FooBarJsonProtocol._
    ...
    get {
      path("foo") {
        complete {
          listOfFoo() //with the implicit in scope this will serialize as json
        }
      }
    } ~ post {
      path("bar") {
        entity(as[Bar]) { bar =>  //extract json Bar from post body
          complete(bar) //serialize bar to json (or whatever processing you want to do)
        }
      }
    }
  }