Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Direct JSON RPC support #621

Closed
FunFunFine opened this issue Jun 19, 2020 · 3 comments
Closed

Direct JSON RPC support #621

FunFunFine opened this issue Jun 19, 2020 · 3 comments
Milestone

Comments

@FunFunFine
Copy link
Contributor

Hello!
In my organization we are using tapir and it feels like a very good tool.

Nevertheless we are also heavily using JSON RPC protocol and tapir doesn't help us at all.

Here is the example:

  • JRPC always uses HTTP POST requests with same top level json body, e.g.
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}
  • method field contains information about API method that request is calling.
  • params field contains actual data needed for the method of API, it can have any structure: list, object, raw value etc.
    As for now we are using Http4s for it and we have to do dynamic dispatch on field method and then decode params as desired object:
case class JRPCRequest[A](id: String,
                      jsonrpc: String,
                      method: String,
                      params: A)

 def handleReq(req: Request[F]): F[Response[F]] = getMethodFromRequest(req) match {
case "foo" => req.as[JRPCRequest[Foo]] >>= (...) some foo handler
case "bar" => req.as[JRPCRequest[Bar]] >>= (...) some bar handler
case _ => ... //bad request here
}

where getMethodFromRequest gets method field from JSON.
All this boilerplate code doesn't look very good and is a pain to support, and I'm not even talking about documentation like swagger etc.

I've tried using tapir somehow by creating two endpoints:

  val jrpcList = endpoint.post.in(jsonBody[JRPCRequest[List[Int]]]).out(stringBody)

  val jrpcInt = endpoint.post.in(jsonBody[JRPCRequest[Int]]).out(stringBody)
def routes[F[_]: Sync: ContextShift]: HttpRoutes[F] = jrpcInt.toRoutes(_ =>
      "Int".asRight[Unit].pure[F]) <+> jrpcList.toRoutes(_ => "List".asRight[Unit].pure[F])

But this didn't work, only Int endpoint have been working while the other's response was "Error: Bad Request"

So is there any possibility to add JSON RPC support directly into tapir?
I would like to implement it but I need some advices on where to start or what are the best options.

Anyway thank you guys for the great library!

@adamw
Copy link
Member

adamw commented Jun 19, 2020

As I understand the differentiator between requests is the method field. And you have a single endpoint, which has the server logic dependent on that method?

I think I would start by defining this single endpoint, deserialising the body into a JRPCRequest[Json] (assuming you are using circe). Then, in the server logic, you could dynamically decide which server logic method to run, depending on the method value.

I don't think open api will be of much help. From OpenAPI's viewpoint (and tapir's currently as well), you have a single endpoint, but with complex and dynamic server logic. You could probably document the input/output, though, as a schema with multiple alternatives.

@adamw adamw added this to the Later milestone Mar 16, 2022
@adamw
Copy link
Member

adamw commented Nov 30, 2022

@ghostbuster91 Isn't this something that armadillo implements?

@adamw
Copy link
Member

adamw commented Jun 15, 2023

This is implemented by https://github.com/input-output-hk/armadillo

@adamw adamw closed this as completed Jun 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants