The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Variant 2 — drogonR Native API (dr_app / dr_get / …)

The native API is what you use when you’re writing a new HTTP service in R and want full control over routes, request parsing, response shape, and middleware. Each request runs an R closure on the main R thread; the I/O loop, parsing, and connection management stay in C++.

For the overall picture see vignette("drogonR", package = "drogonR").


Building an app

dr_app() returns a fresh drogon_app (a mutable environment). Routes are added with dr_get() / dr_post() / dr_put() / dr_delete(); each takes the app, a path pattern, and a handler. The helpers return the app, so they pipe.

library(drogonR)

app <- dr_app() |>
  dr_get   ("/health",                function(req) "ok") |>
  dr_get   ("/users/:id",             function(req) {
    dr_json(list(id = req$params[["id"]]))
  }) |>
  dr_post  ("/users",                 function(req) {
    body <- dr_body(req, as = "json")
    dr_json(list(created = body$name), status = 201L)
  }) |>
  dr_delete("/users/:id",             function(req) {
    dr_response(status = 204L)
  })

Path placeholders accept three syntaxes — :id, <id>, {id} — all interchangeable. Captured values arrive in req$params keyed by name. Duplicate (method, path) registrations warn and overwrite the previous handler.


The req object

A handler is called with one argument: a drogon_request. It exposes fields directly and via small accessor helpers.

Field Type Notes
req$method character(1) "GET" / "POST" / …
req$path character(1) URL path with no query string
req$body character(1) raw body as text (UTF-8); use dr_body() to decode
req$headers named character Drogon lowercases names
req$query named character URL-decoded
req$params named list path placeholder captures

Helpers:


Building responses

A handler may return:

# Plain text — the bare-string shorthand.
function(req) "pong"

# JSON. auto_unbox = TRUE turns length-1 R vectors into JSON scalars
# (so list(ok = TRUE) emits {"ok":true}, not {"ok":[true]}).
function(req) dr_json(list(ok = TRUE))

# Explicit status / headers.
function(req) dr_response(
  body    = '{"reason":"gone"}',
  status  = 410L,
  headers = list("Content-Type" = "application/json"))

# Other helpers:
#   dr_text(...)      — status / custom text headers
#   dr_html(...)      — text/html
#   dr_redirect(loc)  — 302 with a Location header
#   dr_file(path)     — stream a file with auto content-type

Throwing an R error from a handler is allowed: the bridge catches it and returns a 500 with a generic body. To customise that body — log the error, render a JSON error envelope, etc. — register an error handler:

app <- dr_app() |>
  dr_on_error(function(req, err) {
    dr_json(list(error = conditionMessage(err),
                 path  = req$path),
            status = 500L)
  }) |>
  dr_get("/risky", function(req) stop("nope"))

Middleware

dr_use(app, mw) appends a middleware to a chain that runs in registration order before the matched route handler. Each middleware takes (req, nxt): call nxt() to delegate downstream (its return value is the response from the next link), or return your own response to short-circuit.

log_requests <- function(req, nxt) {
  t0  <- Sys.time()
  res <- nxt()
  message(sprintf("%s %s -> %s in %s",
                  req$method, req$path, res$status,
                  format(Sys.time() - t0)))
  res
}

require_auth <- function(req, nxt) {
  if (!identical(dr_header(req, "X-Token"), Sys.getenv("APP_TOKEN"))) {
    return(dr_response(status = 401L, body = "unauthorized"))
  }
  nxt()
}

app <- dr_app() |>
  dr_use(log_requests) |>
  dr_use(require_auth) |>
  dr_get("/secret", function(req) "shh")

nxt()’s return is always normalised to a list with status, body, headers, so middleware can mutate it (e.g. res$headers[["X-Tag"]] <- "y"; res) without checking shape.


Static files

dr_static(app, mount, dir) mounts a directory. Files under it are streamed by Drogon directly from a C++ I/O thread — R is never invoked, Range requests work, content-types are auto-detected, and path traversal (.., absolute paths) is rejected with 403.

app <- dr_app() |>
  dr_static("/assets", "./public") |>
  dr_get   ("/api/ping", function(req) "pong")

Starting and stopping the server

dr_serve(app,
         port    = 8080L,
         threads = 4L,        # I/O worker threads inside Drogon
         workers = 1L)        # forked R worker processes; 1 = in-process

# Drive later's loop on the main thread so handlers actually fire.
repeat later::run_now(timeoutSecs = 3600)

dr_serve() returns immediately after Drogon’s I/O threads start. The later::run_now() loop is what dispatches queued requests onto the main R thread; without it, requests pile up and never run. To stop from another R session: dr_stop().

A few rules:


When to reach for the other variants

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.