Automatically create a scalajs file which will call REST apis defined in a Scala Play! server.

Thomas Flucke 7e39a26bbe Refactored and added tests. 5 năm trước cách đây
library 2ce94613e0 First stable version. 5 năm trước cách đây
plugin 7e39a26bbe Refactored and added tests. 5 năm trước cách đây
project 7e39a26bbe Refactored and added tests. 5 năm trước cách đây
.gitignore 55a5dd726c Added gitignore. 6 năm trước cách đây
README.md 7e39a26bbe Refactored and added tests. 5 năm trước cách đây
build.sbt 7e39a26bbe Refactored and added tests. 5 năm trước cách đây

README.md

Scala REST RPC

What is REST?

Rest is a programming paradigm built around using HTTP methods and status codes to communicate between a client and server.

A RESTful API uses HTTP methods to communicate actions. For example GET is a request for information. PUT means insert or update information. And DELETE removes information.

The path describes the data to operate on. For example /user/123/address might refer to the address of a user with the id 123.

The body of a request contains the details about the request. For example the body of the request PUT /user/123/address might contain a new address to associate with the user.

The response body will often contain the result of the transaction or an error message.

What is RPC?

RPC is a programming paradigm where a program calls a function which then runs on another system. From the client program's perspective, the API looks just like a normal function call. But the arguments to the function are actually sent to the server which the preforms the function and sends the result back to the client.

What is REST RPC?

REST RPC is a plugin which creates an RPC interface over a RESTFUL one.

For example, say a program has an API PUT /user/:id/address which maps to the function addUserAddress(id: Long, addr: Address): User. The client program using REST RPC could then make this function call: addUserAddress(123, myNewAddr)(). The RPC layer would then transform that function call into this HTTP request:

PUT /user/123/address HTTP/1.1
Accept: application/json

{
   "line1": "123 Home Ave.",
   "line2": null,
   "zip_code": 12345,
   "city": "St. Sample",
   "state": "CA"
}

And then transform the server's response into a strongly typed response. All without writing any HTTP manipulation.

Why use REST RPC

Several reasons:

  • Type safety between client and server
  • Cleanliness of client code
  • Simplifies tracking the relationships between front-end and back-end
  • Flexibility in renaming APIs during development

How do I use REST RPC?

First, add the plugin to SBT by inserting this line into project/plugins.sbt:

addSbtPlugin("name.tflucke"       % "sbt-rest-rpc"            % "0.1.0")

Second, add the plugin to your project in build.sbt:

enablePlugins(RestRpc)

/* Or if you are using subprojects */

client.enablePlugins(RestRpc)

Finally, add the API file and parser as an input in build.sbt:

settings(
    Compile / generateJsRoutes / fileInputs += (server / baseDirectory).value.toGlob / "conf" / "routes"
)

/* Or if you are using subprojects */

client.settings(
    Compile / generateJsRoutes / fileInputs += (server / baseDirectory).value.toGlob / "conf" / "routes"
)

What if I don't use the play! framework for my server?

You can still use this plugin! You have two options which will work:

One is to create a routes file similar to the one play! framework uses. Even if it is not used by the server, REST RPC can still parse it to create the wrapper APIs.

Another option is to write an APIParser for whichever tool you use to create your APIs. If you feel like you have something useful and robust, feel free to share and we might add it to the plugin for other users.

What if I my client communicates with multiple servers?

This is not a problem. Add a config file containing a list of APIs for each of the servers or one file with a complete list. REST RPC will generate wrapper functions for each of the APIs for each of the servers so long as the servers do not have two functions with the same signature. (We recommend putting each server in a different package to guarantee that each function will be unique.)

FAQ

Why does the client have to call the function twice?

Because we wanted the client to be able to access underlying HTTP properties if it became useful. For example, instead of calling the API, a program may want to acquire the URL like this:

println(addUserAddress(123, myNewAddr).url)

Outputs:

/user/123/address

Additionally, the second call allows the user to specify HTTP arguments separately from RPC arguments. For example:

addUserAddress(123, myNewAddr)(timeout = 5)

This clear separation between the RPC and HTTP helps both readability and reduce collisions (After all, a user might want an RPC parameter named timeout).

Will this plugin support other frameworks besides the play! framework?

While this plugin in intended to be flexible, there are no immediate plans to support other frameworks at this time.

But we will still happily accept support from the community in this matter.

TODO:

  • Request body support
  • Write tests
  • JVM scala support
  • Java support
  • Clean support for other frameworks besides play!
  • Support for JSON serializers besides json-play (uPickle would be a good first one)
  • Aliasing RPC interfaces
  • Source file parsers