Releases: finagle/finch
Finch 0.11-M2
Finch 0.11-M1
This release is a result of fixing an old known issue when an encoder, resolved for a given type A
doesn't take into an account a Content-Type of A
. This is why, for example, an import io.finch.circe._
makes everything JSON.
This release also made it a new record: 24 contributors! Thank you!
Before looking closely into the changes were made, it's worth mentioning that there is now a Roadmap 1.0 available as a wiki page. It's not super clear yet how long it will take, but at least, we now have a finite list of things to-do.
Why M1?
This is just a first step (marked as M1) towards more type safety in Finch. While the solution released here is not complete, I decided to wrap up a release and let people play with initial bits, while postponing the rest of the things to M2. It's been almost 6 months since the previous release and I feel it's important to do a release (even the M-prefixed one) now, given that besides type-level content type, it also contains a number of improvements for memory and performance.
Please, note that the solution is presented here doesn't allow (doesn't compile) mixing endpoints of different content types in a single call .toService
. While this sounds like a dramatic restriction, this should be viewed as a temporary step towards fully working solution that should a part of 0.11.
That said, please, consider upgrade if you only serve JSON (or anything) within your Finch server. Otherwise, would be reasonable to wait for 0.11.
Content-Type as a Type
There is now a much better way to handle content-types in Finch. A content-type is now a type-level string (see #541) and it does affect an implicit resolution of encoders, so, for example, if you ask for Encode.Text[A]
, and you only have Circe's encoders (JSON encoders) in the scope, your program won't compile. This a huge step towards a better utilization of Scala's type-system and capturing just a little more information in types.
Given that content-type is now a separate concept, which is neither attached to Endpoint
nor Output
, a way to specify it is to explicitly pass a requested content-type to a toService
method call (using toServiceAs
variant).
Please, note that Output.withContentType
is gone since it was just a lie.
Before (broken at runtime):
// helloWorld is a text/plain endpoint that will be encoded by whatever encoder is in the scope
// (eg: if Circe is imported it will be a JSON string)
val api: Service[Request, Response] = (
getUsers :+: postUsers :+: helloWorld
).toService
After (broken at compile time):
val api: Service[Request, Response] = (
getUsers :+: postUsers :+: helloWorld
).toServiceAs[Application.Json]
JSON (i.e., Application.Json
) is a default content-type for the to.Service
call.
Charsets Make Difference
Previously, charsets set on Output
don't make much of difference to the HTTP response service with Finch, rather than changing the Content-Type
header. Now they are propagated to the encoder (see #610). For the sake of type-safety, a charset is now java.nio.charset.Charset
, not String
.
This change implies some default behaviour that should be explained. By default, a charset on Output
is None
so it's not copied over to the HTTP response. Although, an encoder will be using UTF-8 as a default. That said, everything will be encoded as UTF-8, but the Content-Type
header won't be caring information about the charset.
Set charset either on Endpoint
or Output
to make it explicit (and propogated to the HTTP response).
New Endpoints
There are two new endpoints (see #606):
Endpoint.empty[A]
that never matchesroot: Endpoint[Request]
that extracts an entire HTTP requests
Better Performance
Finch now uses Catbird's Rerunnable[A]
instead of Eval[Future[A]]
(see #578) and performs 20-30% better due to a reduced number of allocations.
Better Cats Integration
- There is now an
Alternative
instance forEndpoint
provided out of the box (see #551). - New endpoint
paramsNel
returns Cat'sNonEmptyList
(see #582)
Better Encoding
- It's now possible to JSON stream anything that has a JSON encoder (see #566)
- Anything that has a
cats.Show
instance now derivesEncode.PlainText
and so might be served astext/plain
(see #561)
New Packages
Better Generic Derivation
Endpoint.derive[Foo].fromParams
now takes into an account optional params deriving paramOption
and multi-value params deriving either params
or paramsNel
(see #577).
Updated Dependencies
- Finagle 6.35
- Circe 0.5.0-M2
- Cats 0.6
- Shapeless 2.3
Finch 0.10
This release is about unifying request readers and endpoints (see #524): now there is only Endpoint[A]
abstraction in Finch that takes a request and returns a value of type A
. RequestReader
are just a type alias for Endpoint
for now.
Updated dependencies
- Finagle 6.33
- Circe 0.3
- Cats 0.4.1
Breaking API Changes
RequestReader.flatMap
is gone - use::
instead
New Features
- An
Output
ADT now has a third case - anEmpty
output (see #508) - Request readers are now endpoints (see #524)
- There is now very basic support for
Buf
-streaming (see #515 and example)
As usual, all the docs, best-practices and cookbook recipes have been updated.
Finch 0.9.3
This release fixes a couple of silly bugs that have been introduced during performance optimizations on body
readers as well as put some new examples and best practices.
Finch 0.9.2
This release contains a variety of performance improvements for body readers and extracting endpoints as well as new API for failures.
Upgraded Dependencies
- Finagle 6.31
- Circe 0.2.1
- Cats 0.3.0 (yes! Finch now uses Cats'
Eval
to empower endpoints' laziness)
Breaking API Changes
- All the
finch-core
is now available by a single importio.finch._
. To migrate remove importsio.finch.request.*
from your codebase. io.finch.request.RequestError
has been renamed toio.finch.Error
.x.toFuture
andx.toFutureException
have been removed. UseFuture.value
andFuture.exception
instread.
Deprecations
Output.Failure(map: Map[String, String])
is deprecated in favor ofOutput.Failure(cause: Exception)
and will be removed in 0.9.3.
Performance Improvements
This is the first Finch release with first bits of performance work:
- 30% less allocations and 20% less running time for
body*
readers. - 10% less allocations and 10% less running time for extractors
int
,string
,long
,boolean
.
The bottom line is Finch now gives only 5-6% overhead (both allocations and running time) on top of bare metal Finagle.
New Features
New Failures API
Output.Failure
now wraps Exception
, which might be encoded into an HTTP response by EncodeResponse[Exception]
. See docs for more details.
Pretty Printers for Argonaut and Circe
It's now possible to override the default pretty printer for Circe and Argonaut. For example, the following import defines Circe's encoder that drops null keys in JSON objects. See docs for more details.
import io.finch.circe.dropNullKeys._
New Examples and Best Practices
- There is a new sub-project
examples
that contains always up-to-date Finch examples. - New docs section called "Best Practices" is aimed to collect all the known approaches and techniques that work reasonably good with Finch. Feel free to open a PR to share your experience.
Finch 0.9.1
This release contains a bug fix discovered by @nafg and fixed by @imliar.
Breaking API Changes
This release is source compatible with most of the Finch API 0.9.0 except for Outputs
API.
Before:
val ok: Output[Unit] = Ok
val err: Output[Nothing] = BadRequest
After:
val ok: Output[Unit] = Ok()
val err: Output[Nothing] = BadRequest()
New Features
Finch now provides a very basic instance of EncodeResponse[Map[String, String]]
, which is unlikely useful for production projects but it's helpful to provide this out of the box and be more friendly for newcomers.
Finch 0.9.0
This release is mostly about reworking the Endpoint
API. Thanks to all the amazing contributors who helped to make this happen: @travisbrown, @imliar, @arnihermann, @rpless, @roanta, @longcao and many more!
As usual, all the docs have been upgraded to the current state!
Upgraded Dependencies
- Finagle 6.30
- Circe 0.2.0
Breaking API Changes
- Finagle HTTPx renamed to Finagle HTTP. Use this guide to migrate.
Deprecations
io.finch.route.Router
(deprecated) is renamed toio.finch.Endpoint
/>
and/>>
combinators are deprecated - useEndpoint.apply
insteadio.finch.response
package is deprecated - useio.Finch.Output
instead- HTTP verb matchers
Get
,Post
, etc are deprecated - use functionsget: Endpoint[A] => Endpoint[A]
instead
New Features
Finch ♥ OAuth2
finagle-oauth2 is now supported in Finch! See docs for more details.
UUIDs
java.util.UUID
s are supported out of the box in all the possible settings:
- as request reader
param("uuid").as[UUID]: RequestReader[UUID]
- as endpoint
uuid: Endpoint[UUID]
Output
The new way of capturing the output context: headers, cookies, content type, etc. See outputs for more details.
Error Handling
Brand new methods Endpoint.handle
and Endpoint.rescue
allows to handle the failed future from the endpoint. See error handling for more details.
Finch 0.8.0
This release introduces a bunch of neat features as well as some housekeeping cleanups.
Breaking API Changes
finch-auth
has been removed from the project (see new feature "Basic HTTP Auth")- Type aliases
io.finch.HttpRequest
andio.finch.HttpResponse
were removed (use Finagle's types instead) - The
flatMap
method onRouter
has been replaced withap
Deprecated API
- All the old-style endpoints are deprecated:
io.finch.Endpoint
andio.finch.Endpoint
in favour of coproduct routers - The custom request types are also deprecated (see this note)
- All the implicit classes (the bang! operator) on top of
Service
andFilter
are deprecated
New Packages
finch-circe
- enables circe support in Finchpetstore
- the brand new implementation of the popular demo project
New Features
Basic HTTP Auth
Use the following API in order to enable Basic HTTP Auth on the given Router
.
val r: Router[String] = Router.value("protected")
val authR: Router[String] = basicAuth("username", "password")(r)
Smart Router
s Composition
When compositing two coproduct Router
s the yielded Router
will always be flattened.
val r1: RequestReader[Int :+: String :+: CNil] = Router.value(10) :+: Router.value("s1")
val r2: Router[Boolean :+: String :+: CNil] = Router.value(true) :+: Router.value("s2")
val r3: Router[Int :+: String :+: Boolean :+: String :+: CNil] = r1 :+: r2
Compose RequestReader
s and Router
s
Since 0.8.0 it's possible to compose Router
and RequestReader
together with the ?
combinator.
val r1: RequestReader[Int :: String :: HNil] = param("a").as[Int] :: param("b")
val r2: Router[Boolean] = Router.value(true)
val r3: Router[Boolean :: Int :: String :: HNil] = r2 ? r1
Method Matchers
It's now recommended to use new API for matching the HTTP methods.
// Before
val r1: Router0 = Get / "users"
// After
val r2: Router0 = get("users")
Router
Mapper
There is a new API for mapping routers to either functions A => B
or A => Future[B]
. Use an apply
method on Router
instead of methods />>
and >
, which are going to be deprecated in 0.9.0.
val r1: Router[Int] = get("users" / int) { id: Int => id * 42 }
val r2: Router[String] = get("hello") { Future.value("Hello, World!") }
There is just one downside of this feature: you have to always specify all the types in the function you're passing.
Tail Extractors
For each route extractor (i.e., int
, string
), there is now a corresponding tail extractor that extracts a Seq[A]
from the tail of the current route.
// will match "/users/10/20/30" and extract Seq(10, 20, 30)
val r: Router[Seq[Int]] = "users" / ints
Finch 0.7.0
This release is mostly a result of @travisbrown's heroic efforts on replacing ugly hacks (eg. types /
and ~
) with Shapeless' primitives. As a result, a bunch of API has been marked deprecated.
Some inspiring statistics:
- 17 contributors pushed 136 commits since 0.6.0, which is a new record
- 3 new adopters since 0.6.0, which is a new record
Breaking API Changes
- the
/
type (case class) is removed in favour ofHList
(see details below)
Deprecated API
finch-json
is completely deprecated (and will be removed in 0.8.0) in favourfinch-argonaut
finch-jawn
is deprecated (and will be removed in 0.8.0) in favour other JSON libraries- both
~
type (case class) and~
RequestReader
s compositor are deprecated in favour ofHList
-based readers (see details below) finch-micro
is deprecated (and will be removed in 0.8.0) in favour of coproduct routers (see details below)
New Packages
finch-json4s
- JSON4S support in Finch (contributed by @imliar)
New Infrastructure
Finch is moving towards better stability and performance by integrating well-known tools: ScalaCheck and JMH. Property-based testing helped us to completely cover the JSON packages (and some API fromfinch-core
) with tests. Benchmarking allowed us to establish the initial performance characteristic of the major Finch components.
New Features
Most of the new features have landed in the finch-core
package.
HList
-powered RequestReader
The ::
compositor is now used instead of ~
in the applicative syntax of RequestReader
. The main different is that ::
constructs a RequestReader[A :: B :: HNil]
instead of RequestReader[A ~ B]
. The main advantage of this migration is that a well-known method as
is now can be used more generically, allowing to convert any underlying HList
into a case class.
case class Foo(i: Int, s: String)
val foo: RequestReader[Foo] =
(param("i").as[Int] :: param("s")).as[Foo]
See section "Applicative Syntax" for more details.
HList
-powered Router
The /
case class has been replaced with HList
. This change shouldn't affect end users, except for the cases when /
extractor is used. See section "Composing Routers" for more details.
Coproduct Routers
This is another attempt (as well as finch-micro
) to solve the problem of programming with types that matter. The basic idea behind this is that it's now possible to compose two routers of different types using the :+:
compositor that constructs a Router[C <: Coproduct]
.
val router: Router[Foo :+: Bar :+: CNil] =
(foo: Router[Foo]) :+: (bar: Router[Bar])
The killer feature of coproduct routers is that they can be safely converted into a Finagle services using the toService
method.
val router: Router[String :+: HttpResponse :+: CNil] = ???
val service: Service[HttpRequest, HttpResonse] = router.toService
See section "Coproduct Routers" for more details.
Finch 0.6.0
This release involves several improvements of the core
package as well as a new "easter egg" mode of Finch with codename "Your REST API as a Monad". Although, this release is 100% source-compatible with a previous version.
Please, note that while the old-style io.finch.Endopint
is not deprecated yet, it's highly recommended to consider migration to route combinators.
Deprecations
There is a new naming conversion for RequestReader
s that is heavily inspired by Scala standard library:
- Every
RequiredX
is now justx
, eg.param("a")
- Every
OptionalX
is nowxOption
, eg.headerOption("X-Secret")
There is just one exclusion from this scheme:
RequiredParams
is nowparamsNonEmpty
OptionalParams
is nowparams
New Features
- There is a secret Finch mode available: deal with types that matter (see details bellow)
- The
RequestReader
is now polymorphic in terms of request type (see details bellow) - There are two new
RequestReader
s:fileUpload
andfileUploadOption
that can read an upload (amultipart/form
) param from the request - Both
param
andparamOption
are also looking atmultipart/form
content for param values - Two new combinators
~>
and~~>
are available onRequestReader
(see details bellow) - The
/>
combinator onRouter
is now more useful (see details bellow) - There is router
**
that always matches an entire route
Your REST API as a Monad
There is a secret (ninja) mode available in this release. It's fully described in docs/micro.md. The basic idea is to replace Service
(a function) with RequestReader
(a monad), which basically does the same job but implies insane composability. While it's still an experimental approach, we believe that it solves the "Finch Puzzle" and dramatically simplifies the whole picture. We personally believe that his way is "the way" to write idiomatic and robust Finch code.
Polymorphic RequestReader[A]
a la PRequestReader[R, A]
While this tiny change doesn't affect API, this is a huge step towards flexibility. Code speaks for them self, here is the current picture:
trait PRequestReader[R, A] {
def apply(req: R): Future[A]
}
type RequestReader[A] = PRequestReader[HttpRequest, A]
That said, all the monadic and applicative operations are defined over the output type A
(right associative).
Magic behind ~>
, ~~>
and />
combinators
Both ~>
and ~~>
compositors allow to threat underlying type A ~ B .. ~ Z
as (A, B, ..., Z)
. So, the following code compiles:
def sum(i: Int, j: Int) = i + j
val r: RequestReader[Int] =
param("i").as[Int] ~ param("j").as[Int] ~> sum
Using the ~~>
we can make long calls to the underlying async API (considering that Micro
is RequestReader
):
def fetchUserFromDb(id: Int): Future[User] = ???
def getUser: Micro[User] = RequiredParam("id").as[Int] ~~> fetchUserFromDb
Compositor />
basically implies the same idea but for types A / B / C ... Z
.
By tradition, kudos to awesome contributors: @jenshaase, @imliar and @suls!