Appendix C — Plumber vs. Plumber2 Syntax
Overview
plumber and plumber2 both turn R functions into web APIs, and both define routes with #* annotation comments. plumber2 changes what the handler receives (a parsed body and query instead of req), renames the response object, and adds api(), api_get(), and api_post() for building routers in code.
Syntax Comparison
1. Basic Routing
Plumber
In plumber, annotations define the HTTP method, path, and parameters, and each parameter arrives as a function argument:
#* @get /hello
#* @param name The name to greet
function(name = "World") {
list(message = paste("Hello", name))
}Plumber2
plumber2 keeps the annotations. Query parameters are declared with @query (with a type) and arrive together in the query argument:
#* @get /hello
#* @query name:string The name to greet
function(query) {
list(message = paste("Hello", query$name))
}2. Request Bodies and Errors
Plumber
In plumber, the handler reads the request through req and sets status codes on res:
#* @post /predict
function(req, res) {
data <- req$body
if (!is.data.frame(data)) {
res$status <- 400
return(list(error = "Input must be a data frame"))
}
# ... prediction logic
}Plumber2
plumber2 parses the JSON body before the handler runs and passes it in as body; response replaces res:
#* @post /predict
#* @serializer json
function(body, response) {
if (!is.data.frame(body)) {
response$status <- 400L
return(list(error = "Input must be a data frame"))
}
# ... prediction logic
}3. Summary Table
| Feature | plumber |
plumber2 |
|---|---|---|
| Route definition | #* annotations or pr_get()/pr_post() |
#* annotations or api_get()/api_post() |
| Router object | pr() |
api() |
| Request data | Arguments and req$body |
Parsed query and body arguments |
| Response object | res |
response |
| Parameter types | Documented with @param |
Declared with @query name:type |