Getting started with Zio-Http

Rubik’s Cube with code background
Reading Time: 6 minutes

What is Zio?

ZIO is a functional programming library for building concurrent and asynchronous applications in Scala. It provides a set of composable and type-safe abstractions for managing side effects, such as IO, error handling, and concurrency primitives like fibers, promises, and queues.

ZIO is designed to make it easier to write correct and performant concurrent code by providing a more expressive and composable API for working with side effects. It also has a strong focus on type safety and functional programming principles, which can help prevent many common errors and improve code quality.

ZIO is open-source and has a growing community of users and contributors. It is actively developed and maintained by the ZIO team, which is led by John De Goes.

What is Zio-Http?

ZIO HTTP is a Scala library for building high-performance, type-safe HTTP services. It is built on top of the ZIO functional programming library and provides a purely functional approach to building HTTP servers and clients.

ZIO HTTP offers a number of benefits for developers, including composability, type safety, and testability. It allows developers to easily build and compose HTTP services using familiar functional programming constructs, such as monads and monad transformers.

In addition, ZIO HTTP offers a number of performance optimizations, including support for non-blocking I/O and asynchronous processing. This makes it ideal for building high-performance, scalable web applications.

Overall, ZIO HTTP is a powerful tool for building robust, type-safe, and high-performance HTTP services in Scala.

Akka Http vs Zio Http:-

  • The Akka actor model forms the foundation of Akka HTTP, offering a programming environment that is highly concurrent and distributed. In contrast, the ZIO functional effect system forms the foundation of ZIO HTTP, providing a purely functional concurrency model.
  • In Akka HTTP, streams of data are used to process requests and responses. Conversely, in ZIO HTTP, requests are processed as a single unit and the response is returned as a single value, following a request-response model.
  • ZIO HTTP provides a type-safe HTTP API using Scala’s type system, while Akka HTTP uses a DSL for defining routes, which can be less type-safe.
  • Akka HTTP is known for its high performance and low overhead, due in part to its use of the Akka actor model. ZIO HTTP is also highly performant, but its performance characteristics may differ based on the specific use case and workload.
  • While both ZIO and Akka have their own learning curve, Akka’s actor model can be more complex for newcomers, while ZIO’s functional programming approach may require some familiarity with functional concepts.

Both ZIO HTTP and Akka HTTP are powerful libraries for building HTTP servers and clients in Scala. The choice between the two depends on the specific requirements of the project and the developer’s familiarity with functional programming and actor-based concurrency models.

Installation:-

We can set it up by adding the dependency to build.sbt:

libraryDependencies += "dev.zio" %% "zio-http" % "0.0.5"

Routing:-

For handling routes, Http Domain has a collect method that accepts different requests and produces responses. Pattern matching on the route is supported by the framework. The example below shows how to create routes:

import zio.http._

val app = Http.collect[Request] {

  case Method.GET -> !! / "car" / "a"  => Response.text("Audi")

  case Method.GET -> !! / "car" / "b"  => Response.text("BMW")

}

Typed routes can also be created. The below example shows how to accept count as Int only.

import zio.http._

val app = Http.collect[Request] {

  case Method.GET -> !! / "car" / int(count)  => Response.text(s"Audi:
 
$count")

}

App composition in HTTP:-

We use multiple operators for App composition in HTTP:

  • Using the ++ operator.
    The way this operator works is, suppose none of the routes match in a, then the control is passed on to the b app.
    This will be clear by the below code snippet:-
 import zio.http._
 
 val a = Http.collect[Request] { case Method.GET -> !! / "Hi"  => Response.ok
 
}
 val b = Http.collect[Request] { case Method.GET -> !! / "Hello"  =>

Response.ok }
 
 val app = a ++ b

  • Using the <> operator. The way it works is, if a fails, then the control is passed on to the b app.
import zio.http._

val a = Http.fail(new Error("ERROR"))

val b = Http.text("Success")

val app = a <> b

Integrating ZIO with HTTP:-

For creating effectful apps, you can use collectZIO and wrap Response with UIO to produce ZIO effect value.

val app = Http.collectZIO[Request] {

  case Method.GET -> !! / "hello" => UIO(Response.text("Hello World"))

}

How to access the HTTP request:-

To access the request use @ as it binds a matched pattern to a variable and can be used while creating a response.

import zio.http._

val app = Http.collectZIO[Request] {

    case req @ Method.GET -> !! / "fruits" / "a"  =>

      UIO(Response.text("URL:" + req.url.path.asString + " Headers: " +

 req.getHeaders))

    case req @ Method.POST -> !! / "fruits" / "a" =>

      req.bodyAsString.map(Response.text(_))

  }

Getting started with a simple HTTP server that can be built using a few lines of code:-

In this example we are writing a simple hello world code using Zio HTTP, to run this example just click on the run button, and you will notice that the server is started on port 9000.

We have defined a port in this code where the server will start, we have used the comment method for handling the routes that accept different requests and produces responses.

Send an HTTP GET/POST request defined for HTTP routes through Postman, for eg: http://localhost:9000/text, or send a POST request http://localhost:9000/Hi. You will see the respective outputs -> “Hello World!” & “HelloHelloHelloHelloHello”.

Developing a Ticket Booking System using Zio HTTP:-

Adding required dependencies:-

We need to add some ZIO-related dependencies in build.sbt, check below for the dependency which we have used-

ZioVersion used -> “2.0.9”

libraryDependencies ++= Seq(

  "dev.zio" %% "zio"         % zioVersion,

  "dev.zio" %% "zio-streams" % zioVersion,

  "dev.zio" %% "zio-kafka"   % "2.0.7",

  "dev.zio" %% "zio-json"    % "0.4.2",

  "dev.zio" %% "zio-dynamodb" % "0.2.6",

  "dev.zio" %% "zio-test"    % zioVersion,

  "dev.zio" %% "zio-actors" % "0.1.0",

  "dev.zio" %% "zio-logging" % "2.1.10",

  "dev.zio" %% "zio-http" % "0.0.4",

  "dev.zio" %% "zio-http-testkit" % "0.0.3",

  "io.d11" %% "zhttp" % "2.0.0-RC11",

  "com.lihaoyi" %% "ujson" % "1.4.2",

  "org.http4s" %% "http4s-circe" % "1.0-234-d1a2b53",

  "io.circe" %% "circe-core" % "0.14.1",

  "io.circe" %% "circe-generic" % "0.14.1",

  "io.circe" %% "circe-parser" % "0.14.1"

)
package booking.actor.system

import booking.handler.actor.ThreatreActor.theatreActor

import booking.model.{Booking, BookingMessage, BookingResponse}

import zhttp.http._

import zhttp.service.Server

import zio._

import zio.actors.ActorSystem

import zio.json.DecoderOps

import zio.json._

import java.util.UUID

object HttpServer extends ZIOAppDefault {

  val port: Int = 9090

  val actorSystem: Task[ActorSystem] = ActorSystem("ticketBookingSystem")

  val app: Http[Any, Throwable, Request, Response] = {

    println("Http App..............")

    Http.collectZIO[Request] { case req@Method.POST -> !! / "booking" =>

      println("Http App Request Body............"+req.body.asString)

      val bookingZio: ZIO[Any, Throwable, Either[String, Booking]] =

 req.body.asString.map(_.fromJson[Booking])

      var bookingResponse = BookingResponse(None)

      for {

        bookingEither <- bookingZio

        response <- bookingEither match {

          case Left(e) => ZIO.debug(s"Failed to parse the input data:

 $e").as(

            Response.text(e).setStatus(Status.BadRequest)

          )
          case Right(booking) =>

            val bookingID = UUID.randomUUID().toString

            val newBooking = booking.copy(uuid = Some(bookingID))

            for {

              theaterSystem <- actorSystem.flatMap(x =>

 x.make("ticketBookingflowActor", zio.actors.Supervisor.none, (),

 theatreActor))

              response <- theaterSystem ? BookingMessage(newBooking)

            } yield {

              println("Response From the Actor System............... " +

 response)
              Response.json(response.toJson).setStatus(Status.Ok)

            }

        }

      } yield response

    }

  }

    val program: ZIO[Any, Throwable, ExitCode] = for {

      _ <- Console.printLine(s"Starting server on http://localhost:$port")

      _ <- Server.start(port, app)

    } yield ExitCode.success

    override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] =
     
 program

  }

In the above code, we are defining a port where the service will run and creating an actor system, then we are calling the HTTP post method in the app to get the request body from the user, after we have received the req the format will be of JSON type so we are parsing the JSON to use it further.

Since the parsed JSON is of type Either[String, Booking] we will handle both the failed and success scenarios with the help of for-comprehension.

For the failed case we are returning the response with the error we encountered and set the status as bad req, For success case we are generating a new UUID and creating a new booking case class, using the new case class we are calling the actor system which will call two child actors that will get the payment details and persist those datails through booking sink actor in dynamoDB, then we are handling response from the actor system and setting the status to OK.

For more details regarding the working of the actor system please refer to this link -> https://blog.knoldus.com/how-to-develop-event-driven-application-using-zio-actors/

Conclusion:-

In conclusion, ZIO HTTP is a powerful and efficient library for building HTTP applications in Scala. By leveraging the power of the ZIO functional programming library, ZIO HTTP offers a type-safe and composable approach to building HTTP servers and clients. With its seamless integration with other ZIO modules, ZIO HTTP allows for easy handling of asynchronous and concurrent operations, making it an ideal choice for building high-performance and scalable web applications.

Overall, ZIO HTTP is a great choice for developers looking for a modern and efficient way to build HTTP applications in Scala. Its powerful features and ease of use make it an ideal solution for a wide range of use cases, from small microservices to large-scale enterprise applications.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading