Join the Shiny Community every month at Shiny Gatherings

R Plumber New Thumbnail

How to Make an R REST API: A Beginners Guide to Plumber

Updated: November 1. 2022

R REST API with Plumber

REST APIs are everywhere around us. Software engineers use them to develop backend logic, and data scientists use them to deploy machine learning models. But what exactly is a REST API and should you as a data professional know how to code them from scratch? The answer is unquestionably yes, and you’ll why (and how) shortly.

Today you’ll learn how to make a basic R REST API with the plumber package. It’s an R package that makes it easy to write R REST APIs and documentation around them. In this article, we’ll make a REST API around the well-known Gapminder dataset.

Completely new to R? Check out our detailed R tutorial for programmers.

Jump to a section:

Introduction to REST APIs

REST stands for “Representational State Transfer”. In simpler words, it represents a set of rules that developers follow when creating APIs. The most common rule is that you should get a piece of data (response) whenever you make a request to a particular URL.

The request you make is made of four components:

  • Endpoint – a part of the URL you visit. For example, the endpoint of the URL is /predict
  • Method – a type of request you’re sending, can be either GET, POST, PUT, PATCH, and DELETE. They are used to perform one of these actions: Create, Read, Update, Delete (CRUD)
  • Headers – used for providing information (think authentication credentials, for example). They are provided as key-value pairs
  • Body – information that is sent to the server. Used only when not making GET requests.

Most of the time, the response returned after making a request is in JSON format. The alternative format is XML, but JSON is more common. You can also return other objects, such as images instead. You’ll learn how to do that today.

R allows you to develop REST APIs with the plumber package. You can read the official documentation here.

It’s easy to repurpose any R script file to an API with plumber, because you only have to decorate your functions with comments. You’ll see all about it in a bit.

Develop a Simple R REST API with Plumber

To start, create an empty R script file. You’ll need a couple of packages:

  • plumber – to develop the API
  • dplyr – to filter datasets based on the request body (or URL params)
  • ggplot2 – for data visualization
  • gapminder – for data. That’s what you’ll use as a basis for the API.

You can place two roxigen2-like comments for specifying API title and description. These two aren’t mandatory, but you shouldn’t skip them. Here’s how the entire code snippet (imports, name, and description) looks like:


#* @apiTitle Gapminder API
#* @apiDescription API for exploring Gapminder dataset

You’re now ready to create your first endpoint.

Endpoint 1 – /countries

The idea behind this endpoint is that it should return countries and their respective data after a couple of filters are applied. To be more precise, this endpoint accepts parameter values for continent, life expectancy, and population. Value for continent must be exact, and values for the other two parameters filter data so that only rows with greater values are returned.

If you want to do things right, it’ll require many comments, as you’ve seen previously. It’s a good practice to write a short description, and list the parameters, and it’s mandatory to specify the request type and the endpoint.

Below the comments, you’ll place a function that performs the necessary logic and returns the results.

Let’s think about the parameters for a second. You’ll need:

  • continent – column continent
  • life expectancy – column lifeExp
  • population – column pop

All three are mandatory, and you can do the filtering based on the parameter values with the dplyr package. This endpoint will return data for the most recent year only, which is 2007.

Here’s the complete logic behind /countries endpoint:

#* Returns countries that satisfy condition
#* @param in_continent
#* @param in_lifeExpGT Life expectancy greater than
#* @param in_popGT Population greater than
#* @get /countries
function(in_continent, in_lifeExpGT, in_popGT) {
  gapminder %>%
      year == 2007,
      continent == in_continent,
      lifeExp > in_lifeExpGT,
      pop > in_popGT

If you were to run the API now, this is what you’d see:

Image 1 - API documentations page

Image 1 – API documentations page

The endpoint on the bottom of the image (blue box) is clickable. Clicking on it expands a whole another section:

Image 2 - Documentation for the /countries endpoint

Image 2 – Documentation for the /countries endpoint

You can click on the “Try it out” button to make a request straight from the browser. You’ll have to fill in the parameter values then; let’s say like this:

Image 3 - Testing out /countries endpoint

Image 3 – Testing out /countries endpoint

Once you click on the “Execute” button, the response will show below. Here’s what it looks like in this case:

Image 4 - /countries endpoint example response

Image 4 – /countries endpoint example response

And that’s your first endpoint! It takes data in and returns data out. But what if you want to return something else? Like an image, for example. You’ll learn how to do that next.

Endpoint 2 – /plot

This endpoint will be quite different. The goal now is to return an image instead of raw data. The image will contain a line plot made with ggplot2, showing life expectancy over time.

Two parameters are required – country and chart title – both are self-explanatory.

If you want to return an image from an API with R, you’ll have to put the following comment: #* @serializer contentType list(type='image/png'). Everything else is more or less the same.

Regarding the visualization, a subset is made from the original dataset containing only records for the specified country. A simple line and marker plot are then made from the dataset.

The problem is – you can’t return a ggplot2 visualization. You’ll have to save the image with the ggsave() function and then return it with the readBin() function.

Here’s the entire snippet:

#* Returns a line plot of life expectancy for country
#* @param in_country
#* @param in_title Chart title
#* @get /plot
#* @serializer contentType list(type='image/png')
function(in_country, in_title) {
  subset <- gapminder %>%
    filter(country == in_country)
  plot <- ggplot(subset, aes(x = year, y = lifeExp)) +
    geom_line(color = "#0099f9", size = 2) +
    geom_point(color = "#0099f9", size = 5) +
    ggtitle(in_title) +
    theme_classic() +
    theme(aspect.ratio = 9 / 16)
  file <- "plot.png"
  ggsave(file, plot)
  readBin(file, "raw", n =$size)

If you were to run the API now, a new endpoint would immediately catch your attention:

Image 5 - /plot endpoint

Image 5 – /plot endpoint

Here’s how its documentation looks like:

Image 6 - /plot endpoint documentation

Image 6 – /plot endpoint documentation

You can once again click on the “Try it out” button to test the functionality. Let’s see how life expectancy changed over time in Poland:

Image 7 - Testing out the /plot endpoint for life expectancy in Poland

Image 7 – Testing out the /plot endpoint for life expectancy in Poland

Once you click on the “Execute” button, you’ll be presented with the following visualization:

Image 8 - Life expectancy in Poland over time

Image 8 – Life expectancy in Poland over time

And that’s how you can return an image in the API response. You’ve only created endpoints with the GET method so far. You’ll learn how to work with POST next.

Endpoint 3 – /calculate_gdp

You’ll now learn how to work with POST methods (or any other sends data in the request body). The goal is to create another endpoint that calculates the total GDP for a specified country in the latest year (2007).

The only parameter you’ll need is the country.

Once you have this value, you can use dplyr and the summarize() function to calculate the total GDP. Here’s the entire code snippet:

#* Returns most recent GDP for a country
#* @param in_country
#* @post /calculate_gdp
function(in_country) {
  gapminder %>%
      year == 2007,
      country == in_country
    ) %>%
    summarize(gdp = pop * gdpPercap)

If you were to run the API now, you would instantly see a new box, which is green this time, indicating the POST method:

Image 9 - /calculate_gdp endpoint

Image 9 – /calculate_gdp endpoint

You can once again click on the “Try it out” button to test the functionality:

Image 10 - Testing out the /calculate_gdp endpoint

Image 10 – Testing out the /calculate_gdp endpoint

Let’s see what was the total GDP of Poland in 2007:

Image 11 - Testing out the /calculate_gdp endpoint for Poland

Image 11 – Testing out the /calculate_gdp endpoint for Poland

Once you click on the “Execute” button, you’ll be presented with the following response:

Image 12 - Total GDP in Poland in 2007

Image 12 – Total GDP in Poland in 2007

The only difference here between GET and POST is that you can’t put parameters and their values in the URL for the POST. The parameters and values are passed in the request body as JSON.

You now know how to wrap your R code into a simple REST API. Let’s wrap things up next.

Summing up R REST API

You’ve learned a lot today – what REST APIs are, what’s the deal with the plumber package, and how to use it to build basic APIs in the R programming language. 

APIs in R are commonly developed for exposing the predictive functionality of machine learning models. You can do other things, of course (as demonstrated today), but R probably isn’t the best language for doing so. You’re probably better of with Java, Go, or JavaScript if that’s the case.

Learn More