RPlumber API - returning data as CSV instead of JSON - works locally on mac, but not on ubuntu-16.04

682 views Asked by At

We are using RPlumber to host an API, and our developers asked that the API endpoints provide data in a CSV format, rather than JSON. To handle this, we have the following:

r_endpoints.R

#* @get /test-endpoint-1
testEndpoint <- function(res) {
  mydata <- data.frame(a = c(1,2,3), b = c(3,4,5))
  print('mydata')
  print(mydata)
  con <- textConnection("val","w")
  print(paste0('con: ', con))
  write.csv(x = mydata, con, row.names = FALSE)
  close(con)

  print('res and res.body')
  print(res);  
  res$body <- paste(val, collapse="\n")
  print(res$body)
  return(res)
}

#* @get /test-endpoint-2
testEndpoint2 <- function() {
  mydata <- data.frame(a = c(1,2,3), b = c(3,4,5))
  return(mydata)
}

run_api.r

library(plumber)
pr <- plumber::plumb("r_endpoints.R")
pr$run(host = "0.0.0.0", port = 8004)

test-endpoint-2 returns the data in a JSON format, whereas test-endpoint-1 returns the data in a CSV format. When these endpoints are run locally on my mac, and when I hit the endpoints, I receive the following correct output:

enter image description here

To host the API, we've installed R + the libraries + pm2 on a Linode Ubuntu 16.04 server, and installed all (I think all) of the dependencies. When we try to hit the endpoints as hosted on the server, we receive:

enter image description here

Here are the print statements that I've added to test-endpoint-1 to help with debugging:

[1] "mydata"
  a b
1 1 3
2 2 4
3 3 5
[1] "con: 3"
[1] "res and res.body"
<PlumberResponse>
  Public:
    body: NULL
    clone: function (deep = FALSE) 
    headers: list
    initialize: function (serializer = serializer_json()) 
    removeCookie: function (name, path, http = FALSE, secure = FALSE, same_site = FALSE, 
    serializer: function (val, req, res, errorHandler) 
    setCookie: function (name, value, path, expiration = FALSE, http = FALSE, 
    setHeader: function (name, value) 
    status: 200
    toResponse: function () 
[1] "\"a\",\"b\"\n1,3\n2,4\n3,5"

These are the correct print statements - the same that we get locally. For some reason, the server will not allow us to return in a CSV format in the same way that my local machine allows, and I have no idea why this is the case, or how to fix it.

Edit

After updating the plumber library on my local machine, I now receive the error An exception occurred. on my local machine as well. It seems, in the newer version of plumber, that the snippet of code I use to convert the API endpoint output to a CSV file:

  ...
  con <- textConnection("val","w")
  write.csv(x = mydata, con, row.names = FALSE)
  close(con)

  res$body <- paste(val, collapse="\n")
  return(res)

no longer works.

Edit 2

Here's my own stackoverflow post from nearly 3 years ago on how to return the data as a CSV... seems to no longer work.

Edit 3

Using @serialize csv does "work", but when I hit the endpoint, the data is downloaded as a CSV onto my local machine, whereas it would be better for the data to simply be returned in a CSV format from the API, but not automatically downloaded into a CSV file...

3

There are 3 answers

3
Bruno Tremblay On BEST ANSWER

Maybe look into this for inspiration, here I'm modifying responses content-type headers to text/plain. text/plain should display in the browser I believe.

#* @get /json
#* @serializer unboxedJSON
function() {
  dostuff()
}

#* @get /csv
#* @serializer csv list(type="text/plain; charset=UTF-8")
function() {
  dostuff()
}


dostuff <- function() {
  mtcars
}
0
Bruno Tremblay On

This ugly code works

EDIT : added an enum spec for swagger UI

library(plumber)

#* @get /iris
function(type, res) {
  if (type == "csv") {
    res$serializer <- serializer_csv(type = "text/plain; charset=UTF-8")
  }
  iris
}

#* @plumber
function(pr) {
  pr_set_api_spec(pr, function(spec) {
    spec$paths$`/iris`$get$parameters[[1]]$schema$enum = c("json", "csv")
    spec
  })
}
0
blairj09 On

The An exception occurred issue is actually from httpuv and is fixed in the latest GitHub version of the package (see https://github.com/rstudio/httpuv/pull/289). Installing httpuv from GitHub (remotes::install_github("rstudio/httpuv")) and running the API again should resolve the issue.