Skip to content

Latest commit

 

History

History
300 lines (246 loc) · 7.51 KB

README.md

File metadata and controls

300 lines (246 loc) · 7.51 KB

{mariobox}

Lifecycle: experimental CRAN status R-CMD-check

[DISCLAIMER] This is a Work In Progress, please use at your own risk.

The goal of {mariobox} is to provide a framework for packaging {plumber} APIs. Think of it as the “{golem} for {plumber}

Installation

You can install the development version of {mariobox} from GitHub with:

# install.packages("remotes")
remotes::install_github("ThinkR-Open/mariobox")

Example

library(mariobox)

Creating a new {mariobox} project:

path_pipo <- tempfile(pattern = "pipo")

# The create mariobox function will generate a new prefilled project
# with everything you need to get your started
create_mariobox(
  path = path_pipo,
  open = FALSE
)
  ── Creating dir ────────────────────────────────────────────────────────────────
  ✔ Creating '/tmp/RtmpjFWgLO/pipo1d75618a38d99/'Setting active project to '/tmp/RtmpjFWgLO/pipo1d75618a38d99'Creating 'R/'Writing a sentinel file '.here'Build robust paths within your project via `here::here()`Learn more at <https://here.r-lib.org>Setting active project to '<no active project>'Created package directory
  ── Copying package skeleton ────────────────────────────────────────────────────
  ✔ Copied app skeleton
  ── Done ────────────────────────────────────────────────────────────────────────
  A new mariobox named pipo1d75618a38d99 was created at /tmp/RtmpjFWgLO/pipo1d75618a38d99 .

By default, you’ll find the following structure:

fs::dir_tree(path_pipo)
  /tmp/RtmpjFWgLO/pipo1d75618a38d99
  ├── DESCRIPTION
  ├── NAMESPACE
  ├── R
  │   ├── get_health.R
  │   └── run_plumber.R
  ├── dev
  │   └── run_dev.R
  ├── inst
  │   └── mariobox.yml
  ├── man
  │   └── run_api.Rd
  └── tests
      ├── testthat
      │   ├── test-health.R
      │   └── test-run_plumber.R
      └── testthat.R

The default API comes with one default endpoint, /health, which returns a 200 with the “ok” text.

We’ve made the choice to organise your API inside a YAML, so that you can be as close as possible to a package structure. {mariobox} will then do a little bit of its magic and parse this YAML to build the {plumber} API.

  metadata:
     title: mariobox API
   handles:
     health_get:
       methods: GET
       path: /health
       handler: get_health

Add/Remove endpoints

{mariobox} comes with a series of functions to add endpoints to your app. The generic one is add_endpoint, that allows you to pass any HTTP verb, and add_get and friends are wrappers around this function.

add_endpoint(
  name = "allo",
  method = "GET",
  open = FALSE,
  pkg = path_pipo
)
  ✔ Endpoint added

This will produce the following R file:

  #' GET allo
  #' 
  #' @param req,res HTTP objects
  #' 
  #' @export
  #'  
  get_allo <- function(req, res){
      mariobox::mario_log(
          method = "GET",
          name = "allo"
      )
      get_allo_f()
  }
   
  #' GET allo internal
  #' 
  #' @noRd
  #'  
  get_allo_f <- function(){
      return('ok')
  }
add_get(
  name = "hey",
  open = FALSE,
  pkg = path_pipo
)
  ✔ Endpoint added
fs::dir_tree(path_pipo)
  /tmp/RtmpjFWgLO/pipo1d75618a38d99
  ├── DESCRIPTION
  ├── NAMESPACE
  ├── R
  │   ├── get_allo.R
  │   ├── get_health.R
  │   ├── get_hey.R
  │   └── run_plumber.R
  ├── dev
  │   └── run_dev.R
  ├── inst
  │   └── mariobox.yml
  ├── man
  │   └── run_api.Rd
  └── tests
      ├── testthat
      │   ├── test-get_allo.R
      │   ├── test-get_hey.R
      │   ├── test-health.R
      │   └── test-run_plumber.R
      └── testthat.R

The YALML is automatically updated:

  metadata:
     title: mariobox API
   handles:
     health_get:
       methods: GET
       path: /health
       handler: get_health
     allo_get:
       methods: GET
       path: /allo
       handler: get_allo
     hey_get:
       methods: GET
       path: /hey
       handler: get_hey
remove_endpoint(
  name = "allo",
  method = "GET",
  pkg = path_pipo
)
  ✔ Endpoint removed
fs::dir_tree(path_pipo)
  /tmp/RtmpjFWgLO/pipo1d75618a38d99
  ├── DESCRIPTION
  ├── NAMESPACE
  ├── R
  │   ├── get_health.R
  │   ├── get_hey.R
  │   └── run_plumber.R
  ├── dev
  │   └── run_dev.R
  ├── inst
  │   └── mariobox.yml
  ├── man
  │   └── run_api.Rd
  └── tests
      ├── testthat
      │   ├── test-get_allo.R
      │   ├── test-get_hey.R
      │   ├── test-health.R
      │   └── test-run_plumber.R
      └── testthat.R

The YALML is automatically updated:

  metadata:
     title: mariobox API
   handles:
     health_get:
       methods: GET
       path: /health
       handler: get_health
     hey_get:
       methods: GET
       path: /hey
       handler: get_hey

About endpoint functions

All endpoint functions will be in the following format:

METHOD_NAME <- function(req, res) {
  METHOD_NAME_f()
}

METHOD_NAME_f <- function() {
  return("ok")
}

where METHOD is the HTTP verb and name is the name you’ve set in your function.

This format might seem weird, but the idea is to separate the concerns in the following format:

  • METHOD_NAME() will handle the http elements (login, headers..)
  • METHOD_NAME_f() will be a standard function returning data.

That way, you can handle the data manipulation function just like a plain standard one, test it, interact with it, etc, without having to care about the HTTP part.

Running the API

In dev, you can launch the file at dev/run_dev.R.

source("dev/run_dev.R", echo = TRUE)

In production, the run_api() is in charge of running the API.

If you want to deloy to RStudio Connect, the build_plumber_file() function will create a plumber.R file at the root of your folder. You can deploy using this file.

build_plumber_file(pkg = path_pipo)
  ℹ Loading pipo1d75618a38d99plumber.R file created

This will produce the following file:

  library(plumber)
   
  #* @apiTitle mariobox API
   
  pkgload::load_all()
   
   
  #* @get /health
  get_health
   
  #* @get /hey
  get_hey