Skip to contents

Plumber2 allows you to create APIs by merely decorating your existing R code with special annotations. The example below shows a file named plumber.R (the conventional name for Plumber APIs) which defines an API.

#* Echo the parameter that was sent in
#*
#* @get /echo/<msg>
#*
#* @param msg:string The message to echo back.
#*
function(msg) {
  list(
    msg = paste0("The message is: '", msg, "'")
  )
}

#* Plot out data from the palmer penguins dataset
#*
#* @get /plot
#*
#* @query spec:string If provided, filter the data to only this species
#* (e.g. 'Adelie')
#*
#* @serializer png
#*
function(query) {
  myData <- palmerpenguins::penguins
  title <- "All Species"

  # Filter if the species was specified
  if (!is.null(query$spec)){
    title <- paste0("Only the '", query$spec, "' Species")
    myData <- subset(myData, species == query$spec)
  }

  plot(
    myData$flipper_length_mm,
    myData$bill_length_mm,
    main=title,
    xlab="Flipper Length (mm)",
    ylab="Bill Length (mm)"
  )
}

This file defines two Plumber handlers. One is hosted at the path /echo/{msg} and simply echoes the message passed in as the second part of the path; the other is hosted at the path /plot and returns an image showing a simple R plot.

If you haven’t installed plumber2 yet, see the installation section. Once you have plumber2 installed, you can use the api() function to translate this R file into a Plumber API:

papi <- api("plumber.R")
papi

The api object now encapsulates all the logic represented in your plumber.R file. The next step is to bring the API to life using the api_run() method:

papi |>
  api_run()

You should see a message about your API running on your computer on port 8080. The API will continue running in your R session even though you can still interact with the session. To stop the API from running, use api_stop(). If you’re running this code locally on your personal machine, you should be able to open http://localhost:8080/echo/test or http://localhost:8080/plot in a web browser to test your new API endpoints.

If you’re using a tool like RStudio Server to run your R code on a remote machine, you should see the networking section for help with visiting your API.

The /echo/test endpoint should show output resembling the following.

{
  "msg": [
    "The message is: 'test'"
  ]
}

The /plot endpoint will show you a simple plot of some data from the palmerpenguins dataset.

If you see something like the above: congratulations! You’ve just created your first Plumber API! You’ve already exercised your API from a web browser, but there’s nothing stopping you from leveraging this API from third-party tools or a client developed in R or any other programming language.

Specifying the Inputs

You may have noticed that the functions that define our endpoints accept parameters. These parameters allow us to customize the behavior of our endpoints. In the example above we use two different ways of specifying the outputs. For the /echo/... handler we take a variable part of the path and use this as an argument. You can see above that “test” is used in the resulting json object and had we provided a different path, e.g. /echo/plumber then “plumber” would have been used instead. In the other handler we rely on the “query string” to pass in an optional argument. If you visit http://localhost:8080/plot?spec=Adelie, you should see a similar graph to the one you saw before, but now the dataset has been filtered to only include the “Adelie” species in the palmerpenguins dataset.

As you might have guessed, the spec=Adelie portion of the URL sets the spec element in query to Adelie. More details on how Plumber processes inputs are available in the Routing & Input article.

Customizing The Output

In the previous example, you saw one endpoint that rendered into JSON and one that produced an image. However, if you visited the the /echo/test path from a web browser you probably saw the result rendered as HTML. Plumber2 comes with many different serializers that can convert the result of your R code into a variety of formats. It is possible for the client (e.g. the web browser) to request a specific format, and if the server knows the format it will generally oblige. A browser usually prefers HTML, so that request wins over the default JSON representation that plumber2 uses. If you wish to ensure that JSON is always returned you can make that explicit using @serializer json which sets JSON as the only known output format.

For graphical output you have to always be explicit so that plumber knows to set up a graphic device and capture the output. In the example above we use @serializer png but other graphic serializers are available as well, e.g. @serializer jpeg.

While it is neat to be able to provide results in a variety of formats it is unlikely that all formats makes sense to the output you produce so you should generally be mindful of the serializers you provide

#* @get /hello
#* @serializer html
function() {
  list(
    body = list(
      h1 = "hello world"
    )
  )
}

This handler would always produce HTML when called.

<html><body><h1>hello world</h1></body></html>

You can even provide your own custom serializers and define how to translate the R object produced by your handler into the bits that will produce Plumber’s HTTP response.

You can find more details in the Rendering & Output article.