I've made a pretty simple rest-method using Finch and Finagle:
val getUsers:Endpoint[List[User]] = get("users") {Ok(getAllUsers())}
Http.serve(":8080", getUsers.toService)
and got this error:
Error:(50, 32) not enough arguments for method toService: (implicit ts: io.finch.internal.ToService[List[DAL.Instances.User.User]])com.twitter.finagle.Service[com.twitter.finagle.http.Request,com.twitter.finagle.http.Response].
Unspecified value parameter ts.
Http.serve(":8080", getUsers.toService)
^
Any idea on how to fix it?
The compiler error is a little better in the most recent version of Finch (0.10). If we have the following build config:
scalaVersion := "2.11.7"
libraryDependencies += "com.github.finagle" %% "finch-core" % "0.10.0"
And this setup:
import com.twitter.finagle.Http
import io.finch._
case class User(id: String, name: String)
def getAllUsers(): List[User] = List(User("111", "Foo McBar"))
val getUsers: Endpoint[List[User]] = get("users") { Ok(getAllUsers()) }
Then when we try to use toService
, we get the following:
<console>:18: error: You can only convert a router into a Finagle service if the
result type of the router is one of the following:
* A Response
* A value of a type with an EncodeResponse instance
* A coproduct made up of some combination of the above
List[User] does not satisfy the requirement. You may need to provide an
EncodeResponse instance for List[User] (or for some part of List[User]).
Http.serve(":8080", getUsers.toService)
^
The problem is that you haven't told Finch how to translate instances of your User
type into HTTP responses. Finch's EncodeResponse
is an example of a type class, which is an approach to polymorphism that's widely used in Scala (including the standard library) and many other statically-typed functional programming languages.
The easiest way to provide the appropriate EncodeResponse
instances is to add Finch's Circe compatibility module to your build:
libraryDependencies ++= Seq(
"io.circe" %% "circe-generic" % "0.3.0",
"com.github.finagle" %% "finch-circe" % "0.10.0"
)
And then all you need is the following imports:
import io.finch.circe._, io.circe.generic.auto._
And toService
will work just fine:
scala> Http.serve(":8080", getUsers.toService)
Feb 26, 2016 8:32:24 AM com.twitter.finagle.Init$$anonfun$1 apply$mcV$sp
INFO: Finagle version 6.33.0 (rev=21d0ee8b5070b735eda5c84d7aa6fbf1ba7b1635) built at 20160203-202859
res2: com.twitter.finagle.ListeningServer = Group(/0:0:0:0:0:0:0:0:8080)
Now if you go to http://localhost:8080/users
, you'll see the following:
[{"id":"111","name":"Foo McBar"}]
This looks like magic, but what's happening is fairly principled. Circe is a JSON library that provides generic codec derivation that figures out at compile-time how to represent your case classes as JSON values (see my blog post here for more context).
The Finch cookbook is a great resource for learning more about questions like this, and the first section goes into detail about other ways to provide the EncoderResponse
instances that toService
needs. If you have any other questions or if any of the above isn't clear, feel free to ask either here or on Gitter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With