All Projects → gvolpe → http4s-good-practices

gvolpe / http4s-good-practices

Licence: other
Collection of what I consider good practices in Http4s (WIP)

Projects that are alternatives of or similar to http4s-good-practices

Http4s
A minimal, idiomatic Scala interface for HTTP
Stars: ✭ 2,173 (+2836.49%)
Mutual labels:  fs2, http-client, http-server
telegram-bot-fs2
Example telegram bot implementation using fs2 and http4s client (no akka)
Stars: ✭ 41 (-44.59%)
Mutual labels:  http4s, fs2
scala-functional-programming-tutorial
Functional Programming in Scala Tutorial
Stars: ✭ 23 (-68.92%)
Mutual labels:  http4s, fs2
matador
Take your appclication by the horns
Stars: ✭ 59 (-20.27%)
Mutual labels:  http-client, http-server
BCA-Phantom
A multi-platform HTTP(S) Reverse Shell Server and Client in Python 3
Stars: ✭ 80 (+8.11%)
Mutual labels:  http-client, http-server
EthernetWebServer SSL
Simple TLS/SSL Ethernet WebServer, HTTP Client and WebSocket Client library for for AVR, Portenta_H7, Teensy, SAM DUE, SAMD21, SAMD51, STM32F/L/H/G/WB/MP1, nRF52 and RASPBERRY_PI_PICO boards using Ethernet shields W5100, W5200, W5500, ENC28J60 or Teensy 4.1 NativeEthernet/QNEthernet. It now supports Ethernet TLS/SSL Client. The library supports …
Stars: ✭ 40 (-45.95%)
Mutual labels:  http-client, http-server
cpp-rest-api
RESTFul Web service by C++, implemented basic REST endpoints and RESTVerbs (GET,POST,PUT,DELETE).
Stars: ✭ 13 (-82.43%)
Mutual labels:  http-server, restful-api
http4ts
Server as a Function http toolkit for TypeScript & JavaScript
Stars: ✭ 30 (-59.46%)
Mutual labels:  http-client, http-server
HttpServerLite
TCP-based simple HTTP and HTTPS server, written in C#.
Stars: ✭ 44 (-40.54%)
Mutual labels:  http-server, restful-api
wine
A lightweight and flexible framework to help build elegant web API
Stars: ✭ 39 (-47.3%)
Mutual labels:  http-server, restful-api
cpphttpstack
c++ api for http client & server
Stars: ✭ 30 (-59.46%)
Mutual labels:  http-client, http-server
rawhttp
HTTP library to make it easy to deal with raw HTTP.
Stars: ✭ 156 (+110.81%)
Mutual labels:  http-client, http-server
pfhais
Source code of the book Pure functional HTTP APIs in Scala including a chapter about upgrading to Scala 3.
Stars: ✭ 48 (-35.14%)
Mutual labels:  http4s, fs2
open-rest-es6-boilerplate
open-rest boilerplate project with es6
Stars: ✭ 24 (-67.57%)
Mutual labels:  http-server, restful-api
http4s-dom
http4s, in a browser near you
Stars: ✭ 13 (-82.43%)
Mutual labels:  http4s, http-client
go-sse
Fully featured, spec-compliant HTML5 server-sent events library
Stars: ✭ 165 (+122.97%)
Mutual labels:  http-client, http-server
Http Kit
http-kit is a minimalist, event-driven, high-performance Clojure HTTP server/client library with WebSocket and asynchronous support
Stars: ✭ 2,234 (+2918.92%)
Mutual labels:  http-client, http-server
Donkey
Modern Clojure HTTP server and client built for ease of use and performance
Stars: ✭ 199 (+168.92%)
Mutual labels:  http-client, http-server
typelevel-stack.g8
📚 Unofficial Giter8 template for the Typelevel Stack (Http4s / Doobie / Circe / Cats Effect / Fs2) based on Cats v1.x.x
Stars: ✭ 63 (-14.86%)
Mutual labels:  http4s, fs2
angular6-httpclient-example
Angular 6 HttpClient: Consume RESTful API Example
Stars: ✭ 38 (-48.65%)
Mutual labels:  http-client, restful-api

Http4s Good Practices

This is a collection of what I consider good practices that I've been learning along the way, designing and writing APIs using Http4s. Be aware that it could be bias towards my preferences.

Stream App

It is recommended to start the Http Server by extending the given fs2.StreamApp. It'll handle resources cleanup automatically for you. Example:

class HttpServer[F[_]: Effect] extends StreamApp[F] {

  override def stream(args: List[String], requestShutdown: F[Unit]): Stream[F, ExitCode] =
    for {
      exitCode <- BlazeBuilder[F]
                    .bindHttp(8080, "0.0.0.0")
                    .mountService(httpServices)
                    .serve
    } yield exitCode

}

Also notice that I chose to abstract over the effect. This gives you the flexibility to choose your effect implementation only once in exactly one place. For example:

import monix.eval.Task

object Server extends HttpServer[Task]

Or

import cats.effect.IO

object Server extends HttpServer[IO]

Usage of Http Client

Whenever any of your services or HTTP endpoints need to make use of an HTTP Client, make sure that you create only one instance and pass it along.

Given the following service:

class MyService[F[_]: Sync](client: Client[F]) {

  val retrieveSomeData: Stream[F, A] = {
    val request = ???
    client.streaming[Byte](request)(_.body)
  }

}

You'll create the client where you start the HTTP server using the Http1Client.stream (uses Stream.bracket under the hood):

for {
  client   <- Http1Client.stream[F]())
  service  = new MyService[F](client)
  exitCode <- BlazeBuilder[F]
                .bindHttp(8080, "0.0.0.0")
                .serve
} yield exitCode

The same applies to the usage of any of the Fs2 data structures such as Topic, Queue and Promise. Create it on startup and pass it along wherever needed.

HTTP Services Composition

HttpService[F[_]] is an alias for Kleisli[OptionT[F, ?], Request[F], Response[F]] so it is just a function that you can compose. Here's where cats.SemigroupK comes in handy.

Given the following http services, you can combine them into one HttpService using the <+> operator from SemigroupK.

val oneHttpService: HttpService[F] = ???
val twoHttpService: HttpService[F] = ???
val threeHttpService: HttpService[F] = ???

val httpServices: HttpService[F] = (
  oneHttpService <+> twoHttpService <+> threeHttpService
)

NOTE: Don't combine plain HttpService with AuthedService. Use mountService from Server Builder instead for the latter to avoid conflicts since the AuthedService protects an entire namespace and not just an endpoint.

NOTE 2: Since Http4s 0.18.1 you can use AuthMiddleware.withFallThrough(authUser) allowing you to combine plain services with authenticated services.

HTTP Middleware Composition

HttpMiddleware[F[_]] is also a plain function. Basically an alias for HttpService[F] => HttpService[F]. So you can compose it.

def middleware: HttpMiddleware[F] = {
  {(service: HttpService[F]) => GZip(service)(F)} compose
    { service => AutoSlash(service)(F) }
}

Fs2 Scheduler

It's a very common practice to have an fs2.Scheduler as an implicit parameter that many of your services might use to manage time sensible processes. So it makes sense to create it at the the server startup time:

override def stream(args: List[String], requestShutdown: F[Unit]): Stream[F, ExitCode] =
  Scheduler(corePoolSize = 2).flatMap { implicit scheduler =>
    for {
      exitCode <- BlazeBuilder[F]
                    .bindHttp(8080, "0.0.0.0")
                    .serve
    } yield exitCode
  }

Encoders / Decoders

Http4s exposes two interfaces to encode and decode data, namely EntityDecoder[F, A] and EntityEncoder[F, A]. And most of the time Json is the data type you're going to be working with. Here's where Circe shines and integrates very well.

Circe

You need 3 extra dependencies: circe-core, circe-generic and http4s-circe. One option is to define the json codecs in the package object where you define all your HttpServices. This is my setup with a workaround (see http4s/http4s#1648):

import cats.effect.Sync
import io.circe.{Decoder, Encoder}
import org.http4s.{EntityDecoder, EntityEncoder}
import org.http4s.circe.{jsonEncoderOf, jsonOf}

package object http {
  implicit def jsonDecoder[F[_]: Sync, A <: Product: Decoder]: EntityDecoder[F, A] = jsonOf[F, A]
  implicit def jsonEncoder[F[_]: Sync, A <: Product: Encoder]: EntityEncoder[F, A] = jsonEncoderOf[F, A]
}

And then you can use the case class auto derivation feature by just importing io.circe.generic.auto._ in your HttpServices.

If you also want to support value classes out of the box, these two codecs will be helpful (you need an extra dependency circe-generic-extras):

import io.circe.generic.extras.decoding.UnwrappedDecoder
import io.circe.generic.extras.encoding.UnwrappedEncoder

implicit def valueClassEncoder[A: UnwrappedEncoder]: Encoder[A] = implicitly
implicit def valueClassDecoder[A: UnwrappedDecoder]: Decoder[A] = implicitly

Streaming Json Parsers

Error Handling

  • MonadError -> F[A]
  • Either -> F[Error Either A]
  • Both Either and MonadError (adapt to Throwable)

Authentication

  • AuthedService[T, F[_]]
  • AuthedMiddleware[F[_], T]
  • AuthedRequest[F[_], T]
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].