Skip to Content
Course content

205: Embedding R Analyses in Web Applications

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, so you've got your analysis working in a script or a notebook. It's great, it's accurate, and it's useful. But here is the problem: your stakeholders don't want to open RStudio. They want a dashboard, a mobile app, or a corporate portal. You can't just email them a .Rmd file and call it "integrated." We need to get your R logic to live inside a web application.

I used to think the only way to do this was through a massive Shiny app, but that's often overkill if you already have a frontend team building a site in React or Vue. Instead, let's look at how to turn your R analysis into a "service" that a web app can talk to. I'm going to walk through how I'd set this up using plumber.

The "It works in my console" phase

Let's say we've built a simple function that predicts the "Risk Score" of a loan application based on a few variables. It's a basic linear model, but the logic is sound.

# My core analysis function
calculate_risk <- function(credit_score, loan_amount) {
  # Simple dummy logic for the sake of the demo
  risk <- (loan_amount / 1000) - (credit_score / 100)
  return(list(risk_score = risk, status = ifelse(risk > 10, "High", "Low")))
}

# I test it here, and it works perfectly.
calculate_risk(650, 15000)

Great. But this is just sitting in my global environment. If a JavaScript frontend wants to get this value, it can't just "call" an R function. It needs an HTTP endpoint—a URL it can send data to and get a response from.

Trying to expose the logic

I'll pull in plumber and try to wrap this function. The idea is to use special comments (decorators) that tell Plumber, "Hey, this function should be a web route."

# risk_api.R
library(plumber)

#* @get /risk
function(credit_score, loan_amount) {
  calculate_risk(credit_score, loan_amount)
}

I run this, start the server, and try to hit http://localhost:8000/risk?credit_score=650&loan_amount=15000. It works! But wait—I notice something annoying. If I forget to pass loan_amount in the URL, the whole thing crashes with a 500 error. That's a terrible user experience for whoever is building the frontend. They'll just tell me the "R backend is broken."

Hardening the interface

I need to stop trusting the input. Web requests are messy; they send everything as strings or omit fields entirely. I'll rewrite the endpoint to handle defaults and type conversion. I'll also switch from a GET request to a POST request because, in a real app, you're usually sending a JSON object with the data, not hanging variables off the end of a URL.

#* @post /predict
#* @param data:json The loan application data
function(req) {
  # Plumber puts the JSON body in req$body
  input <- req$body
  
  # I'll add a bit of validation here
  if (is.null(input$credit_score) || is.null(input$loan_amount)) {
    res$status <- 400 # Bad Request
    return(list(error = "Missing required fields: credit_score and loan_amount"))
  }
  
  # Ensure they are numeric, because JSON can be tricky
  score <- as.numeric(input$credit_score)
  amount <- as.numeric(input$loan_amount)
  
  calculate_risk(score, amount)
}

Now, when I send a POST request with a JSON body, it feels like a professional API. The R logic is totally decoupled from the delivery mechanism. I don't care if the frontend is a fancy React app or a simple Python script; they just send JSON, and my R analysis returns JSON.

The "Deployment" Reality Check

Here is where I usually hit a wall: how do I keep this running? If I close RStudio, the API dies. In a production environment, you wouldn't run this manually. You'd likely wrap this in a Docker container.

The flow looks like this:

  • R Code: Your analysis logic.
  • Plumber: The "translator" that turns R functions into HTTP endpoints.
  • Docker: The "box" that keeps R and all its dependencies installed so it runs the same on your laptop as it does on a Linux server.
  • Web App: The frontend that calls POST /predict and displays the result to the user.

By separating the analysis (the "what") from the application (the "how"), you avoid the nightmare of trying to rewrite your complex R statistics in JavaScript. You keep the power of R, but you give it a face that the rest of the web can understand.




📋 Practical Task

Building a Real-Time Currency Volatility Endpoint

You have been asked to provide a "Volatility Check" for a fintech dashboard. Your goal is to create a Plumber API that takes a numeric price_history (provided as a comma-separated string in a GET request) and returns the standard deviation of those prices.

Requirements:

  • Create a file named volatility_api.R.
  • Define a GET endpoint at /volatility.
  • The function must:
    1. Take a parameter called prices (e.g., "1.21,1.22,1.19,1.25").
    2. Convert that string into a numeric vector.
    3. Calculate the standard deviation using sd().
    4. Return a list containing the standard_deviation and a label status ("Volatile" if sd > 0.05, otherwise "Stable").
  • Include a basic check to return an error message if the prices parameter is missing.

Test your work: Run the API and navigate to:
http://localhost:8000/volatility?prices=1.21,1.22,1.19,1.25

Rating
0 0

There are no comments for now.

to be the first to leave a comment.