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.
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").
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.
req objectA 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:
dr_header(req, "Content-Type") — case-insensitive
header lookup (returns NULL if absent).dr_query(req) — full named character vector; or
dr_query(req, "page") to pull one value
(NULL if absent).dr_body(req, as = "text" | "json" | "raw") —
"json" parses with jsonlite::fromJSON();
"raw" returns a raw vector.A handler may return:
text/plain; charset=utf-8, status 200;dr_response() list — full control
over status, body, headers;dr_response().# 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-typeThrowing 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"))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.
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.
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:
dr_serve()
after dr_stop() errors; Drogon’s event loop can’t be
restarted in the same process.workers > 1 spawns forked R worker
processes that share the listening socket via SO_REUSEPORT.
The supervising R session becomes a watchdog; you still poll
later::run_now() on workers.dr_status() /
dr_running() report current state.drogonR.h, register it with
dr_get_cpp() and skip the R-thread hop — see
vignette("mode-cpp-shared", package = "drogonR").vignette("mode-plumber-shim", package = "drogonR").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.