Skip to content

Instantly share code, notes, and snippets.

@mbbx6spp
Last active February 5, 2022 22:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbbx6spp/b4566aa3baa6ab036d226b91c0b46a8d to your computer and use it in GitHub Desktop.
Save mbbx6spp/b4566aa3baa6ab036d226b91c0b46a8d to your computer and use it in GitHub Desktop.

Expected Output

When running without errors it outputs something like the following to stdout:

IpInfo(X1.X2.X3.X4)

Equivalent in JavaScript

fetch("https://jsonip.com", {headers: { "Accept": "application/json" }})
  .catch(err => { status: err })
  .then(resp => resp.json())
  .then(console.log)

Except that this JS doesn't check the form of the JSON returned conforms to the expected structure.

In async/await code structure it would be like this:

const onFailure = err => { status: err }
const resp = await fetch("https://jsonip.com", {headers: { "Accept": "application/json" }}).catch(onFailure)
const json = resp.json()
console.log(json)

Again there is not decoding the response body to check it has the structure the client expects.

Equivalent in TypeScript with response body structure checking

import * as t from 'io-ts'

const IpInfo = t.type({
  status: t.number
})

type IpInfo = t.TypeOf<typeof IpInfo>

const onFailure = err => { status: err }
const resp = await fetch("https://jsonip.com", {headers: { "Accept": "application/json" }}).catch(onFailure)
const json = resp.json()
const result = IpInfo.decode(json)
console.log(result)
scalaVersion := "3.1.0"
libraryDependencies ++= Seq(
"io.circe" %% "circe-generic" % "0.14.1",
"org.http4s" %% "http4s-client" % "1.0.0-M30",
"org.scastie" %% "runtime-scala" % "1.0.0-SNAPSHOT",
"org.typelevel" %% "cats-core" % "2.7.0",
"org.http4s" %% "http4s-core" % "1.0.0-M30",
"org.http4s" %% "http4s-blaze-client" % "1.0.0-M30",
"org.http4s" %% "http4s-circe" % "1.0.0-M30",
"org.typelevel" %% "cats-effect" % "3.3.3"
)
import cats.effect.{IO, IOApp, ExitCode}
import io.circe.generic.auto._
import org.http4s.Status._
import org.http4s.blaze.client.BlazeClientBuilder
import org.http4s.circe._
import org.http4s.client.Client
import org.http4s.syntax.all._
sealed trait IpResult
case class IpInfo(ip: String) extends IpResult
case class IpError(status: Int) extends IpResult
object HttpClient extends IOApp {
def runClient(client: Client[IO]): IO[IpResult] =
client.get(uri"https://jsonip.com") {
case Successful(body) => body.decodeJson[IpInfo]
case resp => IO.pure(IpError(resp.status.code))
}
override def run(args: List[String]) =
BlazeClientBuilder[IO].resource.use(runClient).flatMap(IO.println).as(ExitCode.Success)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment