// GET /users/:name?minAge=[age] -> List[User]
final case class User(name: String, age: Int)
what we want
(name: String, minAge: Int) => F[List[User]]
api as a type - path
// type Path = "users".type - no can do
val usersW = shapeless.Witness("users")
sealed trait Path[P]
type users = Path[usersW.T]
api as a type - segment
val nameW = shapeless.Witness('name)
sealed trait Segment[K, V]
type name = Segment[nameW.T, String]
api as a type - query and method
// query
val minAgeW = shapeless.Witness('minAge)
sealed trait Query[K, V]
type filter = Query[minAgeW.T, Int]
// method
sealed trait Get[A]
api as a type - put it all together
val Api = api(Get[List[User]],
Root / "users" / Segment[String]('name),
Queries.add(Query[Int]('minAge)))
//Api: ApiTypeCarrier[Get[List[User]] :: name :: users :: ... :: HNil]
let's derive a client - type representation
expected input: KIn <: HList and VIn <: HList
output: O
method: M
elements of our api: El <: HList
what we will have
type KIn = nameW.T :: minAgeW.T :: HNil
type VIn = String :: Int :: HNil
type El = usersW.T :: SegmentInput :: QueryInput :: HNil
type M = GetCall
type O = List[User]
fold over types
trait FoldLeftFunction[In, Agg] { type Out }
implicit def queryFold
[K <: Symbol, V, El <: HList, KIn <: HList, VIn <: HList, M, Out] =
FoldLeftFunction[Query[K, V], (El, KIn, VIn, M, Out)] {
type Out = (QueryInput :: El, K :: KIn, V :: VIn, M, Out)
}
// the rest ...
fold over types
trait TypeLevelFoldLeft[H <: HList, Agg] { type Out }
implicit def returnCase[Agg] = new TypeLevelFoldLeft[HNil, Agg] {
type Out = Agg
}
implicit def foldCase[H, T <: HList, Agg, FfOut, FOut]
(implicit f: FoldLeftFunction.Aux[H, Agg, FfOut],
next: Lazy[TypeLevelFoldLeft.Aux[T, FfOut, FOut]]) =
new TypeLevelFoldLeft[H :: T, Agg] { type Out = FOut }
collect the request data
type Uri = List[String]
type Queries = Map[String, List[String]]
VIn => (Uri, Queries)
val Api = api(
Get[List[User]],
Root / "users" / Segment[String]('name),
Queries add Query[Int]('minAge)
)
val get = derive(Api)
get("joe", 42).run[IO](client)
Typedapi - what you saw and more
val endpoints = derive[IO](Api).from((name, age) => ???)
val server = mount(sm, endpoints)
server.unsafeRunSync()