plumber - Convert your existing R code to a web API
Author: Jeff Allen

Ï

# plumber

### Turn R code into web APIs

Jeff Allen / @trestleJeff
http://plumber.trestletech.com Slides hosted at http://plumber.trestletech.com/components/earl-2015/

## You've got a website...

## You've got R code...

    buildModel <- function(myData){
      mcmcTheKmeansLogarithm(myData)
    }
    forecastSales <- function(date){
      linearModel(sales, date)
    }

## You want to combine them...

## Plumber can help!

- Convert existing R code to web APIs
- Just add comments to your existing code
- Leverage R from existing websites and APIs

# Outline

- Decorating
- Running
- Routing
- Filtering

# Outline

- Decorating
- Running
- Routing
- Filtering

## Decorating

    makePlot <- function(){
      par(mar = rep(2, 4)) # margins
      dates <- seq(as.Date("2015-10-01"),
          as.Date("2015-10-31"), by=1)
      sold <- 1:31

      plot(dates, sold, type="b", main="Sales")
    }

## Decorating

    #* @get /salesgraph
    #* @png
    makePlot <- function(){
      par(mar = rep(2, 4)) # margins
      dates <- seq(as.Date("2015-10-01"),
          as.Date("2015-10-31"), by=1)
      sold <- 1:31

      plot(dates, sold, type="b", main="Sales")
    }

## Decorating

    sales <- NULL
    #* @post /addsale
    function(qty, item, id){
      sales <<- rbind(sales, data.frame(
        id = id,
        time = Sys.time(),
        item = item,
        qty = qty
      ))
      id
    }

## Automatic Parsing

    GET plumber.trestletech.com/sale?qty=2

    #* @get /sale
    function(qty){
      qty == "2" # TRUE
    }

## POST Forms too!

    POST plumber.trestletech.com/ HTTP/1.1

    qty=3

    #* @post /
    function(qty){
      qty == "3" # TRUE
    }

## You always get `req` and `res`

    #* @get /
    function(req, res){
      ip <- req$REMOTE_ADDR
      res$setHeader("Last-Modified",
            "Fri, 16 Oct 2015 12:45:26 GMT")
      # ...
    }

# Outline

- Decorating
- Running
- Routing
- Filtering

## Running

    library(plumber)

    pr <- plumber::plumb("myfile.R")
    pr$run(port=8000)

# Demo

- Decorating
- Running
- Routing
- Filtering

## Static Routes

    #* @post /transaction
    function(item, qty, id){
      sales <<- rbind(sales, list(time=Sys.time(),
                       item=item, qty=qty, id=id))
    }

    POST myplumber.com/transaction HTTP/1.1

    item=black-hat&qty=3&id=429

## Variable Routes

    #* @get /transaction/<id>
    function(id){
      id <- as.integer(id)
      sales[sales$id == id, ]
    }

    GET myplumber.com/transaction/429 HTTP/1.1

    [{"id":429,"time":"2015-10-16 20:52:06",
        "item":"blue-hat","qty":"3"}]

## Typed Variable Routes

    #* @get /transaction/<id:int>
    function(id){
      sales[sales$id == id, ]
    }

    GET myplumber.com/transaction/429 HTTP/1.1

    [{"id":429,"time":"2015-10-16 20:52:06",
        "item":"blue-hat","qty":"3"}]

## Complex Routes

    #* @get /hats/<color>/size/<size:int>
    function(color, size){
      hats[hats$color == color
            & hats$size == size]
    }

# Outline

- Decorating
- Running
- Routing
- Filtering

## Endpoints

[Always serve things]

    #* @get /transaction/<id>
    function(id){
      id <- as.integer(id)
      sales[sales$id == id, ]
    }

## Special Endpoints

[ Serve Images ]

    #* @get /transaction/plot
    #* @png
    function(id){
      plot(sales$date, sales$qty,
        main="Qty/Purchase Over Time",
        xlab="Date", ylab="Qty")
    }

Returns a PNG image of your graph

## Filters

[ 1. Passively do things ]

    #* @filter logger
    function(req){
      print(paste0(date(), " - ",
              req$REMOTE_ADDR, " - ",
              req$REQUEST_METHOD, " ",
              req$PATH_INFO))
      forward()
    }

## Filters

[ 2. Change things on the way by ]

    #* @filter setuser
    function(req){
      un <- req$HTTP_COOKIE
      # Make req$username available to endpoints
      req$username <- un

      forward()
    }

## Filters

[ 3. Can serve a response ]

    #* @filter nochrome
    function(req){
        if (!grepl("Chrome", req$HTTP_USER_AGENT)){
          forward()
        } else {
          res$status <- 400
          res$body <- "NOT WELCOME HERE!"
        }
    }

# Extras

- Debug your web app on a REPL!
- Static asset server
- Helpers to serve HTML, Markdown, and RMD
- Composable API, if annotations aren't your thing

## Go Forward and make your dreams come true!

- Website integration
- Internal analytics APIs
- Webhooks (GitHub, Google Drive, Slack, ...)
- Include a web-accessible demo in your packages
- Complete website hosted in R