jsonrpc4cats

Http4s Integration

Dependencies

Add the following dependencies to your build.sbt:

libraryDependencies ++= Seq(
  "io.github.m3is0" %% "jsonrpc4cats-circe" % "0.7.0",
  "io.github.m3is0" %% "jsonrpc4cats-http4s" % "0.7.0"
)

A Basic Example

An example showing how to integrate jsonrpc4cats with http4s and its built-in authentication:

package jsonrpc4cats.example

import cats.Applicative
import cats.data.Kleisli
import cats.data.OptionT
import cats.effect.IO
import io.circe.Json
import org.http4s.HttpApp
import org.http4s.server.AuthMiddleware
import org.http4s.server.Router

import jsonrpc4cats.RpcErr
import jsonrpc4cats.circe.given
import jsonrpc4cats.http4s.RpcService
import jsonrpc4cats.server.RpcMethod
import jsonrpc4cats.server.RpcServer

object PublicRpc {

  // the HTTP response will be '200 OK' for any valid request
  def sum[F[_]](using F: Applicative[F]) =
    RpcMethod.instance[F, "pub.sum", (Int, Int), RpcErr, Long] { (a, b) =>
      F.pure(Right(a.toLong + b.toLong))
    }

  def api[F[_]: Applicative] =
    RpcServer
      .add(sum[F])
}

object SecuredRpc {

  final case class User(name: String, isAdmin: Boolean)

  // the HTTP response will be '200 OK' for any User
  def sum[F[_]](using F: Applicative[F]) =
    RpcMethod.withAuth[F, User, "sec.sum", (Int, Int), RpcErr, String] {
      case (user, (a, b)) =>
        F.pure(Right(s"${user.name}: ${a.toLong + b.toLong}"))
    }

  // the HTTP response will be '401 Unauthorized' if User.isAdmin == false
  def multiply[F[_]](using F: Applicative[F]) =
    RpcMethod.withAuthIf[F, User, "sec.mul", (Int, Int), RpcErr, String](_.isAdmin) {
      case (user, (a, b)) =>
        F.pure(Right(s"${user.name}: ${a.toLong * b.toLong}"))
    }

  def api[F[_]: Applicative] =
    RpcServer
      .add(sum[F])
      .add(multiply[F])
}

object Http4sApp {

  import SecuredRpc.User

  // a dummy AuthMiddleware
  val authMiddleware: AuthMiddleware[IO, User] =
    AuthMiddleware[IO, User](
      Kleisli(_ => OptionT.pure[IO](User("User123", false)))
    )

  // HttpApp, which can be run with Http4s
  val httpApp: HttpApp[IO] =
    Router(
      "/rpc" -> RpcService.httpRoutes[Json](PublicRpc.api[IO]),
      "/secured/rpc" -> authMiddleware(RpcService.authedRoutes[Json](SecuredRpc.api[IO]))
    ).orNotFound
}

Request Requirements

Response Codes