Suppose you’ve developed a very useful algorithm or statistical model and you need to integrate it with some external system. Nowadays HTTP became de facto a lingua-franca for this kind of tasks.
In this article we will demonstrate how to use RestRserve to build a basic REST API.
Generally RestRserve workflow consists of several major steps:
Application$new()
request
and response
as an input. request
and response
are instances of RestRserve::Request
and RestRserve::Response
. It is important to remember that both request
and response
are mutable objects.response
in place or raise()
exception in case of errorlibrary(RestRserve)
= Application$new() app
For simplicity we will use Fibonacci number calculation as an algorithm we want to expose.
= function(n) {
calc_fib if (n < 0L) stop("n should be >= 0")
if (n == 0L) return(0L)
if (n == 1L || n == 2L) return(1L)
= rep(1L, n)
x
for (i in 3L:n) {
= x[[i - 1]] + x[[i - 2]]
x[[i]]
}
return(x[[n]])
}
Create function which will handle requests.
= function(.req, .res) {
fib_handler = as.integer(.req$parameters_query[["n"]])
n if (length(n) == 0L || is.na(n)) {
raise(HTTPError$bad_request())
}$set_body(as.character(calc_fib(n)))
.res$set_content_type("text/plain")
.res }
You may have noticed strange .req
and .res
argument names. Starting from RestRserve
v0.4.0 these “reserved” names allows to benefit from autocomplete:
Technically .req
and .res
are just empty instances of ?Request
and ?Response
classes exported by RestRserve
in order to make autocomplete work.
$add_get(path = "/fib", FUN = fib_handler) app
Now we can test our application without starting it:
= Request$new(path = "/fib", parameters_query = list(n = 10))
request = app$process_request(request)
response
cat("Response status:", response$status)
#> Response status: 200 OK
cat("Response body:", response$body)
#> Response body: 55
It is generally a good idea to write unit tests against application. One can use a common framework such as tinytest.
Generally it is a good idea to provide documentation along with the API. Convenient way to do that is to supply a openapi specification. This as simple as adding a yaml file as an additional endpoint:
openapi: 3.0.1
info:
title: RestRserve OpenAPI
version: '1.0'
servers:
- url: /
paths:
/fib:
get:
description: Calculates Fibonacci number
parameters:
- name: "n"
description: "x for Fibonnacci number"
in: query
schema:
type: integer
example: 10
required: true
responses:
200:
description: API response
content:
text/plain:
schema:
type: string
example: 5
400:
description: Bad Request
= system.file("examples", "openapi", "openapi.yaml", package = "RestRserve")
yaml_file $add_openapi(path = "/openapi.yaml", file_path = yaml_file)
app$add_swagger_ui(path = "/doc", path_openapi = "/openapi.yaml", use_cdn = TRUE) app
Now all is ready and we can start application with Rserve backend. It will block R session and start listening for incoming requests.
= BackendRserve$new()
backend $start(app, http_port = 8080) backend
Send request to calculate fibonacci number:
curl localhost:8080/fib?n=10
Check out a swagger UI in the browser: http://localhost:8080/doc