pheymann / typedapi   0.2.0

MIT License GitHub

Build your web API on the type level.

Scala versions: 2.12 2.11
Scala.js versions: 0.6

Build Status Maven Central Gitter Scala.js

experimental project: see issues #39 and #41

Typedapi

Define type safe APIs and let the Scala compiler do the rest:

Api definition

import typedapi._ val MyApi = // GET {body: User} /fetch/user?{name: String} api(method = Get[MT.`application/json`, User], path = Root / "fetch" / "user", queries = Queries add Query[String]('name)) :|: // POST {body: User} /create/user apiWithBody(method = Post[MT.`application/json`, User], body = ReqBody[Json, User], path = Root / "create" / "user")

And for the Servant lovers:

import typedapi.dsl._ val MyApi = // GET {body: User} /fetch/user?{name: String} (:= :> "fetch" :> "user" :> Query[String]('name) :> Get[MT.`application/json`, User]) :|: // POST {body: User} /create/user (:= :> "create" :> "user" :> ReqBody[Json, User] :> Post[MT.`application/json`, User])

Client side

import typedapi.client._ val (fetch, create) = deriveAll(MyApi) import typedapi.client.http4s._; import cats.effect.IO; import org.http4s.client.blaze.Http1Client val cm = ClientManager(Http1Client[IO]().unsafeRunSync, "http://my-host", 8080) fetch("joe").run[IO](cm): IO[User]

Server side

import typedapi.server._ val fetch: String => IO[Result[User]] = name => findUserIO(name).map(success) val create: User => IO[Result[User]] = user => createUserIO(user).map(success) val endpoints = deriveAll[IO](MyApi).from(fetch, create) import typedapi.server.http4s._; import cats.effect.IO; import org.http4s.server.blaze.BlazeBuilder val sm = ServerManager(BlazeBuilder[IO], "http://my-host", 8080) val server = mount(sm, endpoints) server.unsafeRunSync()

This is all you have to do to define an API with multiple endpoints and to create a working client and server for them.

You can find the above code as a complete project here.

Motivation

This library is the result of the following questions:

How much can we encode on the type level? Are we able to describe a whole API and generate the call functions from that without using Macros?

It is inspired by Servant and it provides an API layer which is independent of the underlying server/client implementation. Right now Typedapi supports:

If you need something else take a look at this doc.

Get this library

It is available for Scala 2.11, 2.12 and ScalaJS and can be downloaded as Maven artifact:

// dsl "com.github.pheymann" %% "typedapi-client" % <version> "com.github.pheymann" %% "typedapi-server" % <version> // http4s support "com.github.pheymann" %% "typedapi-http4s-client" % <version> "com.github.pheymann" %% "typedapi-http4s-server" % <version> // akka-http support "com.github.pheymann" %% "typedapi-akka-http-client" % <version> "com.github.pheymann" %% "typedapi-akka-http-server" % <version> // Scalaj-Http client support "com.github.pheymann" %% "typedapi-scalaj-http-client" % <version> // ScalaJS client support "com.github.pheymann" %% "typedapi-js-client" % <version> 

You can also build it on your machine:

git clone https://github.com/pheymann/typedapi.git cd typedapi sbt "+ publishLocal" 

Ammonite

Typedapi also offers an improved experience for Ammonite and ScalaScripts:

import $ivy.`com.github.pheymann::typedapi-ammonite-client:<version>` import typedapi._ import client._ import amm._ val Readme = api(Get[MT.`text/html`, String], Root / "pheymann" / "typedapi" / "master" / "README.md") val readme = derive(Readme) // gives you the raw scalaj-http response val cm = clientManager("https://raw.githubusercontent.com") val response = get().run[Id].raw(cm) response.body response.headers ...

In case Ammonite cannot resolve com.dwijnand:sbt-compat:1.0.0, follow this solution.

Documentation

The documentation is located in docs and covers the following topics so far:

Dependencies

Contribution

Contributions are highly appreciated. If you find a bug or you are missing the support for a specific client/server library consider opening a PR with your solution.