Spray is a well-known HTTP library in the Scala ecosystem. It was released in 2011, and since then it’s been widely used by the Scala community. It was recently announced that Spray would be replaced with Akka HTTP, thus cementing Akka HTTP as the successor of Spray. It’s maintained by Lightbend and it’s been recommended that users migrate to it soon.
However, migration from one major version of a library to another is not an easy task. Very often it requires you to spend some time reading the source code in order to figure out how to use certain features, as well as how to migrate existing logic.
This post will demonstrate what changes should be applied in order to migrate your app from Spray to Akka HTTP. The following steps don’t have a particular order, as it depends on which areas need to be rewritten.
¶Packages
In order to have the lastest Akka HTTP packages, all previous Spray dependencies now need to be replaced by the following:
1 | "com.typesafe.akka" %% "akka-http-core" % “2.4.3” |
¶HttpService
Spray’s HttpService has been removed. Use Http class and pass your routes to the bindAndHandle method. For example:
Before:
1 | val service = system.actorOf(Props(new HttpServiceActor(routes))) |
After:
1 | Http().bindAndHandle(routes, "0.0.0.0", port = 8080) |
¶Marshalling
Marshaller.of
can be replaced with Marshaller.withFixedContentType
. See below:
Before:
1 | Marshaller.of[JsonApiObject](`application/vnd.api+json`) { (value, contentType, ctx) => |
After:
1 | Marshaller.withFixedContentType(`application/vnd.api+json`) { obj => |
Akka HTTP marshallers support content negotiation, so you don’t have to specify the content type when creating one “super” marshaller from other marshallers:
Before:
1 | ToResponseMarshaller.oneOf( |
After:
1 | Marshaller.oneOf( |
¶Unmarshalling
The example below shows one way that an Unmarshaller might be rewritten:
Before:
1 | Unmarshaller[Entity](`application/vnd.api+json`) { |
After:
1 | Unmarshaller.stringUnmarshaller.forContentTypes(`application/vnd.api+json`).map(_.parseJson.convertTo[Entity]) |
¶MediaTypes
MediaType.custom
can be replaced with specific methods in MediaType
object.
Before:
1 | MediaType.custom("application/vnd.acme+json") |
After:
1 | MediaType.applicationWithFixedCharset("application/vnd.acme+json", HttpCharsets.`UTF-8`) |
¶Rejection Handling
RejectionHandler now uses a builder pattern – see the example below:
Before:
1 | def rootRejectionHandler = RejectionHandler { |
After:
1 | RejectionHandler |
¶Client
The Spray-client pipeline was removed. Use Http’s singleRequest method instead:
Before:
1 | val pipeline: HttpRequest => Future[HttpResponse] = (addHeader(Authorization(OAuth2BearerToken(accessToken))) ~> sendReceive) |
After:
1 | val request = HttpRequest( |
¶Headers
All HTTP headers have been moved to the akka.http.scaladsl.model.headers._ package.
Form fields and file upload
With the streaming nature of http entity, it’s important to have a strict http entity before accessing multiple form fields or use file upload directives. One solution might be using next directive before working with form fields:
1 | val toStrict: Directive0 = extractRequest flatMap { request => |
And one can use it like this:
1 | toStrict { |
While this list isn’t an exhaustive collection of all the changes you need to do, it covers the trickiest ones that exist. One major drawback of Akka HTTP is that it’s not as mature as Spray, and it’s performance is not optimised yet. Users may also notice a lack of documentation for some cases.
Having said that, it would be a good idea to keep the above issues in mind during this process. Happy migration!