Skip to content

pachadotdev/openapi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

openapi

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

remotes::install_github("pachadotdev/openapi")

Simple example with piping

Paste this into an R console

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

Example without piping

Undefined parameters are NA.

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:

Exporting the spec

# 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

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 ----

api_run(api, host = "0.0.0.0", port = 5000)

# Non-blocking (for development/testing) ----

# 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):

# Error response with custom status
api <- api |>
  api_get("/fail", function() {
    response_error("Something went wrong", status = 400)
  })

Migrating from plumber v1

Before (plumber)

#* @get /hello
function() {
  list(message = "Hello!")
}

#* @param name The name to greet
#* @get /greet
function(name = "World") {
  list(greeting = paste("Hello,", name))
}

After (openapi)

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:

[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:

sudo systemctl daemon-reload
sudo systemctl enable openapi-api
sudo systemctl start openapi-api

License

Apache Licence 2.0

About

An OpenAPI Specification (OAS) compliant REST API framework for R inspired by Python's Flask.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages