R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
228: Building a Simple API with plumber
A few years ago, I worked with a data scientist who had built an incredibly accurate model for predicting warehouse inventory shortages. The model was a masterpiece of R code, but there was one problem: the logistics team didn't know R. They used a custom internal dashboard built in JavaScript. For three months, my colleague was essentially a human API—he would spend his mornings manually running the script for the logistics team, saving the results to a CSV, and emailing it to them. It was a tedious, fragile process that wasted everyone's time. We solved it in an afternoon by wrapping his logic in plumber, turning that script into a live endpoint the dashboard could call directly.
At its core, plumber allows you to take existing R functions and expose them as REST API endpoints. You don't have to write complex networking code or worry about the intricacies of HTTP headers; you simply "decorate" your R functions with special comments that tell plumber how to handle incoming requests.
Decorating Your Logic for the Web
The magic of plumber happens in the comments. Any comment starting with #* is interpreted as a directive. Instead of writing a separate routing file, you write your logic and the API definition in the same place. For instance, if we want to create an endpoint that predicts the yield of a crop based on rainfall, we don't just write the function—we tell plumber that this function should be accessible via a GET request at a specific path.
#* Predict crop yield based on rainfall
#* @param rainfall The amount of rainfall in mm
#* @get /predict-yield
function(rainfall) {
yield <- as.numeric(rainfall) * 0.5 + 10
list(predicted_yield = yield, unit = "bushels per acre")
}
Notice the @get /predict-yield line. This tells the server: "When someone hits the /predict-yield URL using a GET method, run this specific function." I find this approach refreshing because it keeps the documentation and the implementation side-by-side.
Handling Inputs and JSON Responses
One of the biggest headaches in API development is usually data serialization—converting your internal data structures into something the rest of the web understands, usually JSON. Plumber handles this automatically. When your function returns a list or a data frame, plumber serializes it to JSON before sending it back to the client.
You also need to be mindful of how you receive data. In the example above, rainfall is passed as a query parameter (e.g., /predict-yield?rainfall=120). Because URL parameters are transmitted as strings, I explicitly cast it using as.numeric(). If you forget this, you'll likely run into a type-mismatch error the moment you try to perform math on the input. If you're building a more complex API that requires sending a large object, you'll want to use @post instead of @get, which allows the client to send a JSON body.
Spinning Up the Plumber Server
Writing the functions is only half the battle; you actually have to host them. To turn your R script into a running server, you use the plumb() function to parse the file and the prun() function to start the service. I usually keep these calls in a separate run_api.R script so I don't accidentally start the server every time I'm just trying to test a local function.
library(plumber)
# Load the plumber file
pr <- plumb("plumber.R")
# Run the API on a specific port
pr$run(port=8000)
Once this is running, your R session is effectively hijacked to serve as a web server. You can open your browser or a tool like Postman and hit http://localhost:8000/predict-yield?rainfall=100 to see your R function's output rendered as JSON. Just a word of caution: the built-in prun() is great for development, but if you're moving to a production environment with high traffic, you'll want to look into running plumber behind a proxy like Nginx.
📋 Practical Task
Building a Regional Sales Forecasting Endpoint
Your task is to create a simple API using the plumber package that provides a simulated sales forecast for different business regions. This will mimic a real-world scenario where a frontend dashboard needs quick access to R-calculated forecasts.
Requirements:
- Create a file named
forecast_api.R. - Define a function decorated with
@get /forecast. - The function must accept two parameters:
region(a string) andmultiplier(a numeric value). - Inside the function, create a base forecast value (e.g., 1000) and multiply it by the
multiplier. - The function should return a named list containing the
region, thecalculated_forecast, and astatusmessage saying "Success". - Ensure you handle the
multiplierinput by casting it to a numeric type to prevent errors.
Testing your work:
Run the API using plumb("forecast_api.R")$run(port=8000) and verify it works by visiting http://localhost:8000/forecast?region=North&multiplier=1.5 in your browser.
There are no comments for now.