https://github.com/pachadotdev/openapi
An OpenAPI Specification (OAS) compliant REST API framework for R inspired by Python's Flask.
https://github.com/pachadotdev/openapi
Last synced: 15 days ago
JSON representation
An OpenAPI Specification (OAS) compliant REST API framework for R inspired by Python's Flask.
- Host: GitHub
- URL: https://github.com/pachadotdev/openapi
- Owner: pachadotdev
- License: apache-2.0
- Created: 2026-04-28T16:27:12.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-04-28T16:28:03.000Z (2 months ago)
- Last Synced: 2026-05-24T21:32:11.605Z (about 1 month ago)
- Language: R
- Homepage:
- Size: 442 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# openapi
[](https://github.com/USER/REPO/actions/workflows/test-coverage.yaml)
[](https://www.buymeacoffee.com/pacha)
An OpenAPI Specification (OAS) compliant REST API framework for R inspired by Python's Flask.
This is designed to run as a service (e.g., via systemctl on Linux). Everything is returned as
JSON and this package allows to return data from SQL connections or any type of object that can
be serialized as JSON.
## Features
- OpenAPI 3.0 specification auto-generation
- Built-in Swagger UI at `/docs`
- OpenAPI spec served at `/openapi.json`
- Flask-like routing with piping support
- Automatic parameter extraction from query strings
## Installation
```r
remotes::install_github("pachadotdev/openapi")
```
## Simple example with piping
Paste this into an R console
```r
library(openapi)
api <- api_init(
title = "My API",
version = "1.0.0",
description = "A sample API built with openapi for R"
) |>
api_get("/", function() {
list(message = "Welcome to openapi for R!")
},
summary = "Root endpoint",
description = "Returns a welcome message"
) |>
api_get("/hello", function() {
list(greeting = "Hello World!")
},
summary = "Hello endpoint",
tags = c("greetings")
) |>
api_get("/greet", function(name) {
if (is.na(name)) name <- "Moto"
list(greeting = paste("Hello,", name, "!"))
},
summary = "Greet by name",
description = "Greets the user by name, defaults to 'Moto' if not provided",
tags = c("greetings")
)
# Run the server
api_run(api, host = "127.0.0.1", port = 5000, debug = TRUE)
```
then visit
- http://127.0.0.1:5000/ - Your API
- http://127.0.0.1:5000/docs - Swagger UI documentation
- http://127.0.0.1:5000/openapi.json - OpenAPI specification
- http://127.0.0.1:5000/hello
- http://127.0.0.1:5000/greet?name=World
## Example without piping
Undefined parameters are `NA`.
```r
library(openapi)
api <- api_init()
api <- api_get(api, "/mtcars1", function(cyl, am) {
# sanitize parameters
cyl <- as.integer(substr(cyl, 1, 1))
am <- as.integer(substr(am, 1, 1))
d <- mtcars
if (!is.na(cyl) && cyl > 0) { d <- d[d$cyl == cyl, ] }
if (!is.na(am) && am %in% 0:1) { d <- d[d$am == am, ] }
d
})
api <- api_get(api, "/mtcars2", function(cyl, vs) {
# sanitize parameters
cyl <- as.integer(substr(cyl, 1, 1))
vs <- as.integer(substr(vs, 1, 1))
d <- mtcars
if (!is.na(cyl) && cyl > 0) { d <- d[d$cyl == cyl, ] }
if (!is.na(vs) && vs %in% 0:1) { d <- d[d$vs == vs, ] }
d
})
api_run(api, host = "127.0.0.1", port = 5000, debug = TRUE)
```
then visit
http://127.0.0.1:5000/mtcars1?cyl=6
http://127.0.0.1:5000/mtcars1?cyl=4&am=0
etc.
## OpenAPI Specification
The package automatically generates an OpenAPI 3.0 specification from your routes.
### Accessing the spec
When the server is running:
- Swagger UI: http://127.0.0.1:5000/docs
- OpenAPI JSON: http://127.0.0.1:5000/openapi.json
### Exporting the spec
```r
# Get spec as an R list
spec <- api_spec(api, host = "127.0.0.1", port = 5000)
# Write spec to file
api_spec_write(api, path = "openapi.json", host = "127.0.0.1", port = 5000)
```
### Adding metadata to routes
```r
api <- api_init(
title = "My API",
version = "2.0.0",
description = "API description here"
) |>
api_get(
"/users",
function() { list(users = c("alice", "bob")) },
summary = "List all users",
description = "Returns a list of all registered users",
tags = c("users")
) |>
api_post(
"/users",
function(name) { list(created = name) },
summary = "Create a user",
tags = c("users")
)
```
## Running the server
### Blocking (for production)
Runs until Ctrl+C or that you interrupt the API service (see the systemctl example below)
```r
api_run(api, host = "0.0.0.0", port = 5000)
```
### Non-blocking (for development/testing)
```r
# returns modified api, so you must reassign
api <- api_run_background(api, port = 5000)
# do other things
httpuv::service() # process requests while doing other work
# stop when done
api_stop(api)
```
## Response formats
By default, return values are JSON-serialized (lists and data frames work automatically):
```r
# Error response with custom status
api <- api |>
api_get("/fail", function() {
response_error("Something went wrong", status = 400)
})
```
## Migrating from plumber v1
### Before (plumber)
```r
#* @get /hello
function() {
list(message = "Hello!")
}
#* @param name The name to greet
#* @get /greet
function(name = "World") {
list(greeting = paste("Hello,", name))
}
```
### After (openapi)
```r
api <- api_init() |>
api_get("/hello", function() {
list(message = "Hello!")
}) |>
api_get("/greet", function(name) {
if (is.na(name)) name <- "World"
list(greeting = paste("Hello,", name))
})
api_run(api)
```
## systemd service
Create `/etc/systemd/system/openapi-api.service` or similar:
```ini
[Unit]
Description=OpenAPI R API
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/api
ExecStart=/usr/bin/Rscript api.R
Restart=always
[Install]
WantedBy=multi-user.target
```
Then:
```bash
sudo systemctl daemon-reload
sudo systemctl enable openapi-api
sudo systemctl start openapi-api
```
To restart/stop the API:
```bash
sudo systemctl restart openapi-api
sudo systemctl stop openapi-api
```
## License
Apache Licence 2.0