render html from httr::POST inside shiny app?

Is it possible to render the html that i get from httr::POST inside a shiny app? For example, suppose I have the following plumber api:

library(plumber)
library(threejs)

#* Returns threejs htmlwidget 
#* @param data json with x y z
#* @post /threejs
#* @serializer htmlwidget
threejs <- function(data){
  scatterplot3js(data$x,data$y,data$z, color=rainbow(length(data$z)))
}

Using httr i can use that api to generate a plot:

library(shiny)
library(jsonlite)
library(httr)

z <- seq(-10, 10, 0.01)
x <- cos(z)
y <- sin(z)

response <- httr::POST(url = 'http://127.0.0.1:9601/threejs', 
                       body = list(data = list(x = x,
                                   y = y,
                                   z = z)), 
                       encode="json")

cat(content(response, "text"), file="temp.html")

Is there a way to render that inside a shiny app? I tried the following

library(shiny)
library(jsonlite)
library(httr)

z <- seq(-10, 10, 0.01)
x <- cos(z)
y <- sin(z)

response <- httr::POST(url = 'http://127.0.0.1:9601/threejs', 
                       body = list(data = list(x = x,
                                   y = y,
                                   z = z)), 
                       encode="json")

# cat(content(response, "text"), file="temp.html")


#ui
ui <- fluidPage(
  
  # Application title
  titlePanel("Testing Plumber"),
  
  sidebarLayout(
    sidebarPanel(
    ),
    
    mainPanel(
      tags$html(HTML(content(response, "text")))
    )
  )
)

# Define server logic required to draw a histogram
server <- function(input, output) {
  
}

# Run the application 
shinyApp(ui = ui, server = server)

Alas, it does not work as. Thanks for the help!

This seems like a lot of round trip data passing. What is your end goal? Why not render the widget locally and display with htmlwidgets::shinyRenderWidget?


If you must use the plumber api...

I'd look into having a www folder for your shiny application.

Save the output into the www folder and serve that saved file as an iframe.

Thanks @barret, I'm doing this purely as a learning exercise.

Np! Happy learning!


My ideal shiny/plumber/htmlwidget setup would be...

  • Have a shiny app that knows only about the categories of the data... enough to make the UI
  • Make an iframe for the htmlwidget to appear
    • Maybe make this in a renderUI?
  • Change the iframe location to call the plumber API
  • Have the API return a full htmlwidget within that iframe.

Benefits:

  • Shiny will scale really well as the data is not duplicated in memory for each user
  • The data is housed in one location (plumber API)
    • The underlying data can be VERY large, which may not be suitable for a shiny app, but is ok for an R process to solve.
  • The API inner workings can be updated without redeploying the shiny application.

Disadvantages:

  • Two working applications must communicate with each other
  • The data and UI are separated from each other.

Thanks, this is the part I don't really understand how to implement.

In my example content(response, "text") returns a full htmlwidget. Alas, I'm not sure how to change my simple shiny app to add the iframe and render that widget. Could you point me in the right direction?

Use renderUI with an iframe

This is assuming the data is in the plumber API, not Shiny. Also the API should be a GET (rather than a POST) to work with iframes.

#ui
ui <- fluidPage(
  
  # Application title
  titlePanel("Testing Plumber"),
  
  sidebarLayout(
    sidebarPanel(),
    
    mainPanel(
      # ui output goes here
      uiOutput("widget")
    )
  )
)

server <- function(input, output) {
  # Set widget here
  output$widget <- renderUI({
    tags$iframe(
      # this src location could be dynamic
      src = "http://127.0.0.1:9601/threejs"
    )
  })

}

shinyApp(ui = ui, server = server)

Hope it helps!


** Code untested

1 Like

Thanks, this is very useful. My last question is whether is possible to use httr instead of "http://127.0.0.1:9601/threejs". The reason I ask is that I imagine that passing arguments with httr is the best way of making src dynamic. I tried the following:

response <- httr::GET(url = 'http://127.0.0.1:9878/threejs', 
                       encode="json") %>% 
  httr::content("text")

which did not work. If httr is not possible, how would you pass arguments to the plumber api? the only other way i can think of doing it is with glue