This family of functions facilitates adding a request handler for a specific HTTP method and path.
Usage
api_get(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_head(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_post(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_put(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_delete(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_connect(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_options(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_trace(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_patch(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
api_any(
api,
path,
handler,
serializers = NULL,
parsers = NULL,
use_strict_serializer = FALSE,
download = FALSE,
doc = NULL,
route = NULL
)
Arguments
- api
A plumber2 api object to add the handler to
- path
A string giving the path the handler responds to. See Details
- handler
A handler function to call when a request is matched to the path
- serializers
A named list of serializers that can be used to format the response before sending it back to the client. Which one is selected is based on the request
Accept
header. Seeget_serializers()
for a helper to construct this- parsers
A named list of parsers that can be used to parse the request body before passing it in as the
body
argument. Which one is selected is based on the requestContent-Type
header. Seeget_parsers()
for a helper to construct this- use_strict_serializer
By default, if a serializer that respects the requests
Accept
header cannot be found, then the first of the provided ones are used. Setting this toTRUE
will instead send back a406 Not Acceptable
response- download
Should the response mark itself for download instead of being shown inline? Setting this to
TRUE
will set theContent-Disposition
header in the response toattachment
. Setting it to a string is equivalent to setting it toTRUE
but will in addition also set the default filename of the download to the string value- doc
A list with the OpenAPI spec for the endpoint
- route
The route this handler should be added to. Defaults to the last route in the stack. If the route does not exist it will be created as the last route in the stack
HTTP Methods
The HTTP specs provide a selection of specific methods that clients can send to the server (your plumber api). While there is no enforcement that the server follows any conventions you should strive to create a server API that adheres to common expectations. It is not required that a server understands all methods, most often the opposite is true. The HTTP methods are described below, but consider consulting MDN to get acquainted with the HTTP spec in general
GET
: This method is used to request specific content and is perhaps the most ubiquitous method in use.GET
requests should only retrieve data and should not contain any body contentHEAD
: This method is identical toGET
, except the response should only contain headers, no body. Apart from this it is expected that aHEAD
request is identical to aGET
request for the same ressourcePOST
: This method delivers content, in the form of a request body, to the server, potentially causing a change in the server. In the context of plumber2 it is often used to call functions that require input larger than what can be put in the URLPUT
: This method is used to update a specific ressource on the server. In the context of a standard plumber2 server this is rarely relevant, though usage can come up.PUT
is considered by clients to be indemptotent meaning that sending the samePUT
request multiple times have no effectDELETE
: This method deletes a ressource and is the opposite toPUT
. As withPUT
this method has limited use in most standard plumber2 serversCONNECT
: This method request the establishment of a proxy tunnel. It is considered advanced use and is very unlikely to have a usecase for your plumber2 apiOPTIONS
: This method is used by clients to query a server about what methods and other settings are supported on a serverTRACE
: This method is a form of ping that should send a response containing the request (stripped of any sensitive information). Many servers disallow this method due to security concernsPATCH
: This method is likePUT
but allows partial modification of a ressource
Apart from the above, plumber2 also understands the ANY
method which
responds to requests to any of the above methods, assuming that a specific
handler for the method is not found. As the semantics of the various methods
are quite different an ANY
handler should mainly be used for rejections or
for setting specific broad headers on the response, not as the main handler
for the request
The Path
The path defines the URL the request is being made to with the root removed.
If your plumber2 server runs from http://example.com/api/
and a request is
made to http://example.com/api/user/thomas/
, then the path would be
user/thomas/
. Paths can be static like the prior example, or dynamic as
described below:
Path arguments
Consider you have a bunch of users. It would be impractical to register a
handler for each one of them. Instead you can use a dynamic path like with
the following syntax: user/<username>/
. This path would be matched to any
requests made to user/..something../
. The actual value of ..something..
(e.g. thomas
) would be made available to the handler (see below). A path
can contain multiple arguments if needed, such as
user/<username>/settings/<setting>/
Path wildcards
Apart from path arguments it is also possible to be even less specific by
adding a wildcard to the path. The path user/*
will match both
user/thomas/
, user/thomas/settings/interests/
, and anything other path
that begins with user/
. As with arguments a path can contain multiple
wildcards but the use of these have very diminishing returns. Contrary to
path arguments the value(s) corresponding to *
is not made available to the
handler.
Path Priority
With the existence of path arguments and wildcards it is possible that
multiple handlers in a route can be matched to a single request. Since only
one can be selected we need to determine which one wins. The priority is
based on the specificity of the path. Consider a server containing the
following handler paths: user/thomas/
, user/<username>/
,
user/<username>/settings/<setting>/
, user/*
. These paths will have the
following priority:
user/<username>/settings/<setting>/
user/thomas/
user/<username>/
user/*
The first spot is due to the fact that it is the path with the most elements so it is deemed most specific. For the remaining 3 they all have the same number of elements, but static paths are considered more specific than dynamic paths, and path arguments are considered more specific than wildcards.
A request made to user/carl
will thus end up in the third handler, while a
request made to user/thomas
will end up in the second. This ordering makes
it possible to both provide default handlers as well as specialisations for
specific paths.
The Handler
The handler is a standard R function that is called when a request is made that matches the handlers path (unless a more specific handler path exists — see above). A handler function can perform any operation a normal R function can do, though you should consider strongly the security implications of your handler functions. However, there are certain expectations in plumber around the arguments a handler function takes and the return value it provides
Handler Arguments
The handler function can take one or more of the following arguments.
Path arguments: Any path arguments are passed on to the handler. If a handler is registered for the following path
user/<username>/settings/<setting>/
and it handles a request touser/thomas/settings/interests/
then it will be called withusername = "thomas", setting = "interest"
request
: The request the handler is responding to as a reqres::Request objectresponse
: The response being returned to the client as a reqres::Response objectserver
: The Plumber2 object representing your server implementationclient_id
: A string uniquely identifying the session the request comes fromquery
: A list giving any additional arguments passed into the handler as part of the url query stringbody
: The request body, parsed as specified by the provided parsers
Handler Return Value
Handlers can return a range of different value types, which will inform plumber2 what to do next:
Returning Next
or Break
These two control objects informs plumber2 to either proceed handling the
request (Next
) or return the response as is, circumventing any remaining
routes (Break
)
Returning NULL
or the response
object
This is the same as returning Next
, ie. it signals that handling can
proceed
Handler conditions
Like any function in R, a handler may need to signal that something happened,
either by throwing an error or warning or by emitting a message. You can use
stop()
, warning()
, and message()
as you are used to. For all of them,
the condition message will end up in the log. Further, for stop()
any
further handling of the request will end and a 500 Internal Error
response
is returned. To take more control over problems you can use the
abort_*()
family of conditions from reqres. Like stop()
they will halt any further processing, but they also allow control over what
kind of response is sent back, what kind of information about the issue is
communicated to the client, and what kind of information is logged
internally. The response they send back (except for abort_status()
) all
adhere to the HTTP Problem spec defined in
RFC 9457.
While it may feel like a good idea to send a detailed error message back to the client it is often better to only inform the client of what they need to change to solve the issue. Too much information about internal implementation details can be a security risk and forwarding internal errors to a client can help inform the client about how the server has been implemented.
See also
Other Request Handlers:
api_request_header_handlers