Route handler hooks
hooks.RdAdd middleware-style hooks to all existing user handlers in a plumber2::plumber2 API. Hooks execute in a deterministic order prior to the user handler, and can mutate/intercept requests and responses, as well as short-circuit handlers. This allows for middleware-type behaviour without registering additional routes.
Usage
api_hooks(api, hooks, .where = c("append", "prepend"))
hook(id = NULL, fn)Arguments
- api
a plumber2::plumber2 api object.
- hooks
a single hook or list of hooks that take the signature
fn(api, args, next_call), whereargsis the list of handler arguments. If a hook returns without callingnext_call(), the remaining hooks and user handler are skipped, and the return value becomes the handler result.- .where
whether the hooks should be appended or prepended to the list of installed hooks
- id
id of the hook
- fn
function with signature
fn(api, args, next_call)
Details
System hooks are installed in the following order:
freshwater::contextfreshwater::error_pagesfreshwater::csrffreshwater::csrf_context
Control Flow
Hooks control whether subsequent hooks should execute. Concretely:
To continue to the next hook (and eventual user handler), call
next_call().To short-circuit the chain, return a value without calling
next_call().To bubble up to plumber2 routing control flow, return either
plumber2::Nextorplumber2::Break(and don't callnext_call()).
Hooks can also wrap later hooks and the user handler by calling next_call(),
and then doing work after.
Hook Installation
Routing is managed by plumber2::plumber2 – this function does not change routing precedence. Within the handler itself, however, hooks run in the order they are installed.
The function is idempotent (with respect to either a computed hash of the hook or a provided id), and only new hooks will be installed.
Asynchronous Routes
When using asynchronous routes via async=TRUE
programmatically, or via @async, hooks are attached to
the then handlers, rather than main handler itself. This is because
request, response, and server arguments
are not available to the main async
handler, and hooks depend on the
full handler signature being available.
Examples
api <- plumber2::api() |>
plumber2::api_get(path = "/", function() {
"Foo"
})
#> Creating default route in request router
log_hook <- hook(
"logger",
function(api, args, next_call) {
msg <- sprintf(
"[%s] %s %s",
format(Sys.time(), "%H:%M:%S"),
args$request$method,
args$request$path
)
print(msg)
next_call()
}
)
timer_hook <- hook("timer", function(api, args, next_call) {
t0 <- Sys.time()
out <- next_call()
print(sprintf("time: %s", Sys.time() - t0))
out
})
api <- api_hooks(api, hooks = list(log_hook, timer_hook))