Skip to contents

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. See get_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 request Content-Type header. See get_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 to TRUE will instead send back a 406 Not Acceptable response

download

Should the response mark itself for download instead of being shown inline? Setting this to TRUE will set the Content-Disposition header in the response to attachment. Setting it to a string is equivalent to setting it to TRUE 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

Value

These functions return the api object allowing for easy chaining with the pipe

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 content

  • HEAD: This method is identical to GET, except the response should only contain headers, no body. Apart from this it is expected that a HEAD request is identical to a GET request for the same ressource

  • POST: 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 URL

  • PUT: 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 same PUT request multiple times have no effect

  • DELETE: This method deletes a ressource and is the opposite to PUT. As with PUT this method has limited use in most standard plumber2 servers

  • CONNECT: 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 api

  • OPTIONS: This method is used by clients to query a server about what methods and other settings are supported on a server

  • TRACE: 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 concerns

  • PATCH: This method is like PUT 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:

  1. user/<username>/settings/<setting>/

  2. user/thomas/

  3. user/<username>/

  4. 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 to user/thomas/settings/interests/ then it will be called with username = "thomas", setting = "interest"

  • request: The request the handler is responding to as a reqres::Request object

  • response: The response being returned to the client as a reqres::Response object

  • server: The Plumber2 object representing your server implementation

  • client_id: A string uniquely identifying the session the request comes from

  • query: A list giving any additional arguments passed into the handler as part of the url query string

  • body: 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

Returning a ggplot2 object

If you return a ggplot2 object it will get plotted for you (and added to the response assuming a graphics serializer is provided) before handling continues

Returning any other value

Any kind of value returned that is not captured by the above description will be set to the response body (overwritting what was already there) and handling is then allowed to continue

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