The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
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[@]metanet.ch.

Rate limiting

dr_rate_limit() adds a per-route or per-app cap on how many requests are allowed in a rolling time window. Over-budget requests are rejected with HTTP 429 Too Many Requests and a Retry-After header, before the request is dispatched to R — so a flood of clients can’t saturate the dispatcher or your handler.

Quick start

library(drogonR)

app <- dr_app() |>
  dr_get("/health",      function(req) "ok") |>
  dr_get("/api/users",   function(req) "users") |>
  # 100 requests per 60 s, applied to every route under /api/
  dr_rate_limit(capacity = 100L, window = 60, routes = "/api/")

dr_serve(app, port = 8080L)

/health is unaffected (it is outside the /api/ prefix). /api/users is allowed up to 100 hits per 60 s; subsequent hits get 429 until the window slides forward.

How it works

The check runs on Drogon’s I/O thread, immediately after route matching and before the request enters the R-side dispatch pipeline. That means:

The Retry-After header carries the rule’s window (rounded up to seconds) — a conservative upper bound on how long the client should back off.

Algorithms (type =)

dr_rate_limit(app, capacity = 10L, window = 1, type = "sliding_window")
dr_rate_limit(app, capacity = 10L, window = 1, type = "fixed_window")
dr_rate_limit(app, capacity = 10L, window = 1, type = "token_bucket")

All three are implemented by Drogon’s RateLimiter class; drogonR just wraps each instance in a small mutex so the I/O threads can call isAllowed() concurrently without racing.

Scope (scope =)

dr_rate_limit(app, capacity = 10L, window = 1, routes = "/api/",
              scope = "per_route")   # default

dr_rate_limit(app, capacity = 10L, window = 1, routes = "/api/",
              scope = "global")

A route may match several rules at once (dr_rate_limit() is additive). To pass through, the request must satisfy every matching rule.

Prefix matching (routes =)

routes is a character vector of path prefixes:

# all of /api/ AND /admin/, with separate budgets per route
dr_rate_limit(app, capacity = 100L, window = 60,
              routes = c("/api/", "/admin/"))

NULL (the default) matches every registered route — useful for an app-wide cap layered on top of per-area rules.

Per-IP limiting

Not provided by dr_rate_limit(). The bucket is shared across all clients of the matched routes; if you need a per-client cap, do it in front of drogonR (nginx limit_req, Caddy rate_limit, Cloudflare, an API gateway, etc.). Doing per-IP enforcement at the application layer would require maintaining a hash table of clients keyed by source IP and pruning it under contention — a lot of overhead for something a reverse proxy already does well.

Operational notes

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.