{"id":28393764,"url":"https://github.com/mazamascience/beakr","last_synced_at":"2025-06-26T11:32:29.535Z","repository":{"id":35171160,"uuid":"208920905","full_name":"MazamaScience/beakr","owner":"MazamaScience","description":"A Minimalist Web Framework for R","archived":false,"fork":false,"pushed_at":"2023-04-26T17:54:58.000Z","size":919,"stargazers_count":94,"open_issues_count":3,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-06-07T17:13:39.247Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://mazamascience.github.io/beakr/","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MazamaScience.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-09-17T00:08:09.000Z","updated_at":"2025-03-22T10:52:45.000Z","dependencies_parsed_at":"2024-02-09T02:07:07.485Z","dependency_job_id":null,"html_url":"https://github.com/MazamaScience/beakr","commit_stats":{"total_commits":231,"total_committers":5,"mean_commits":46.2,"dds":"0.20779220779220775","last_synced_commit":"7d9d58d4fc6a7c51eee6e6c832380f31d3c153ba"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/MazamaScience/beakr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MazamaScience%2Fbeakr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MazamaScience%2Fbeakr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MazamaScience%2Fbeakr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MazamaScience%2Fbeakr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MazamaScience","download_url":"https://codeload.github.com/MazamaScience/beakr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MazamaScience%2Fbeakr/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262056213,"owners_count":23251626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-31T17:08:06.767Z","updated_at":"2025-06-26T11:32:29.526Z","avatar_url":"https://github.com/MazamaScience.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![sm-beakr.png](https://i.postimg.cc/7YBB0Hnp/sm-beakr.png)](https://postimg.cc/bstHjjMT)\n\n[![CRAN\\_Status\\_Badge](http://www.r-pkg.org/badges/version/beakr)](https://cran.r-project.org/package=beakr)\n[![Downloads](http://cranlogs.r-pkg.org/badges/beakr)](https://cran.r-project.org/package=beakr)\n\n## A Minimalist Web Framework for R\n\n**beakr** is a minimalist web framework for developing web services in the R \nLanguage. **beakr** offers a robust set of fundamental web application features \nand is intended to simplify the development of web services that reflect R \npackage APIs — without obscuring R's data processing capability and ease of use.\n\nA dedicated Slack channel has been created for announcements, support and to help build a community of \npractice around this open source package. You may request an invitation to join from jonathan.callahan@dri.com.\n\n### Hello, world! - The beakr way\n\n```r\nlibrary(beakr)\n\n# Create a new beakr server\nnewBeakr() %\u003e% \n\n  # Respond to GET requests at the \"/hi\" route\n  httpGET(path = \"/hi\", function(req, res, err) {\n    print(\"Hello, World!\")\n  }) %\u003e% \n  \n  # Respond to GET requests at the \"/bye\" route\n  httpGET(path = \"/bye\", function(req, res, err) {\n    print(\"Farewell, my friends.\")\n  }) %\u003e% \n  \n  # Handle any errors with a JSON response\n  handleErrors() %\u003e%\n  \n  # Start the server on port 25118\n  listen(host = \"127.0.0.1\", port = 25118) \n```\n\nA new web service is now available on the local host that responds to two\nURLs:\n\n* http://127.0.0.1:25118/hi\n* http://127.0.0.1:25118/bye\n\n## Overview\n\nThe **beakr** package allows R code to listen for and respond to HTTP requests, \nso you can serve web traffic directly from a _Beakr_ instance. The **beakr** \npackage is intended to be simple, lightweight and unopinionated.  \n\nWhile **beakr** is not recommended for building extensive web frameworks, R and \nthe flexibility of the package are (potentially) up to the task. Keep in mind \nthat **beakr** was not designed to be an especially performant web framework and \nthe  _\"batteries are certainly not included\"_. If you're looking for full \nfeatured  web frameworks, there are better tools and languages for that \n(see [Shiny](https://shiny.rstudio.com), [django](https://www.djangoproject.com), etc.). \n**beakr** is inspired by the minimalist and massively-expandable frameworks \noffered by [Express.js](https://expressjs.com) and \n[Flask](https://palletsprojects.com/p/flask/). \n\nOne of the reasons to use **beakr** is that it is incredibly flexible. It allows \nyou to integrate your R code as _Middleware_ in a _Beakr_ instance. Middleware \nfunctions can execute any R code, make changes to the _Request_, _Response_, and\n_Error_ objects, and then serve up the response at the end the request-response \ncycle. The **beakr** package loosely follows Express.js middleware semantics, \nwhere middleware functions are functions that have access to the _Request_, \n_Response_, and _Error_ objects of a _Beakr_ instance.\n\n_Note:_ By convention, the _Response_, _Request_, and _Error_ objects are always \nreferred to as `res`, `req` and `err`, respectively. See the package documentation \nfor more information.\n\n## Installation\n\nWhen released, you will be able to install the latest release version from CRAN:\n\n```\ninstall.packages(\"beakr\")\n```\n\nOr you can install the latest development version from GitHub: \n\n```\ninstall.packages(\"devtools\")\ndevtools::install_github(\"MazamaScience/beakr\")\n```\n\n## Examples\n\n### A KNN model webservice \n\nA _Beakr_ instance can easily expose R function signatures as webservice APIs. \nAs an example, let's expose a simple machine learning model using the \n[caret](https://github.com/topepo/caret) package and the Iris data set. The \n`predict_species()` function accepts four arguments which it uses to predict the \nspecies of iris associated with incoming data. The _Beakr_ instance \nexposes this API and, when given JSON input with the required arguments, \nidentifies and returns the species.\n\n_Note_ that `httpPOST` attaches the URL path `/predict-species` only to http POST\nrequests. Pointing a browser at this URL path will issue a File Not found error\nbecause the browser is issuing an http GET request. Like other frameworks,\n**beakr** allows for method-specific URL routing.\n\n```r\n# Import libraries \nlibrary(beakr)\nlibrary(caret)\n\n# Load the Iris data set \ndata('iris')\n\n# Train using KNN\nknn_model \u003c- train(\n  Species ~ ., \n  data = iris, \n  method = 'knn', \n  trControl = trainControl(method='cv', number=10), \n  metric = 'Accuracy'\n)\n\n# Function to predict the species using the trained model.\npredict_species \u003c- function(sl, sw, pl, pw) {\n  test \u003c- data.frame(\n    Sepal.Length = as.numeric(sl),\n    Sepal.Width = as.numeric(sw),\n    Petal.Length = as.numeric(pl),\n    Petal.Width = as.numeric(pw),\n    Species = NA\n  )\n  return(predict(knn_model, test))\n}\n\n# Use beakr to expose the model in the \"/predict-species\" url path.\n#   See help(\"decorate\") for more info about decorating functions.\nnewBeakr() %\u003e%\n  httpPOST(path = \"/predict-species\", decorate(predict_species)) %\u003e%\n  handleErrors() %\u003e%\n  listen(host = \"127.0.0.1\", port = 25118)\n```\n\nYou can interact with this webservice by sending an HTTP POST request to  \n`http://127.0.0.1:25118/predict-species` with incoming data supplied as a JSON \nstring containing sepal length and width (`sl`, `sw`) and petal length and width \n(`pl`, `pw`). The _Beakr_ instance responds with the predicted species of iris. \n\n```bash\n$ curl -X POST http://127.0.0.1:25118/predict-species \\\n  -H 'content-type: application/json' \\\n  -d '{ \"sl\": 5.3, \"sw\": 4, \"pl\": 1.6, \"pw\": 0.2 }'\n  \n\u003e setosa\n```\n\n### A state plotting webservice\n\nWe can use a built-in convenience function of a **beakr**'s _Response_ object to \nprint and return a _ggplot_ object. Use `help(\"Response\"\")` to view other \n_Response_ object methods and documentation.  In this example we'll wrap some map \ngeneration code and serve it with a _Beakr_ instance.  Instead of decorating an \nexisting package function, we will create a **beakr**-oriented function \nthat uses a response object method to send back raw image bytes. Parameters in \nthe URL request will be converted into arguments to the function.\n\n```r\nlibrary(beakr)\nlibrary(ggplot2)\n\n# Create a plot of a US state\nstate_plot \u003c- function(state = NULL, res) {\n  states \u003c- ggplot2::map_data('state')\n\n  if ( !is.null(state) ) {\n    states \u003c- subset(states, region == tolower(state))\n  }\n\n  plot \u003c-\n    ggplot(data = states) +\n    geom_polygon(aes(x = long, y = lat, fill = region, group = group), color = \"white\") +\n    coord_fixed(1.3) +\n    guides(fill = FALSE)\n\n  # Pass the plot to the beakrs response plot method\n  res$plot(plot, base64 = FALSE, height = 800, width = 800)\n}\n\n# Create and start a default beakr instance\nnewBeakr() %\u003e%\n  httpGET(path = '/usa', decorate(state_plot)) %\u003e%\n  listen()\n```\n\nView a map of Washington state by visiting: http://127.0.0.1:8080/usa?state=washington.\n\n### A custom webservice\n\nUsers can create custom functions that will be run when specific URLs are\naccessed using specific HTTP methods. The following example provides a basic\noutline for creating more complex webservices:\n\n```r\nlibrary(beakr)\nlibrary(MazamaCoreUtils)\n\nnewBeakr() %\u003e%\n\n  # ----- Welcome --------------------------------------------------------------\n\n  httpGET(\"/\", function(req, res, err) {\n\n    response \u003c-\n\"\n\u003chtml\u003e\n\u003cbody\u003e\n\u003ch1\u003eWelcome to repeater!\u003c/h1\u003e\n\u003cp\u003eURL paths look like \u003ccode\u003e/repeater?text=...\u0026amp;times=...\u0026amp;responseType=...\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eThe following \u003ccode\u003eresponseTypes\u003c/code\u003e are supported:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003etxt\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ejson\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n\"\n    return(response)\n\n  }) %\u003e%\n\n  # ----- Repeater -------------------------------------------------------------\n\n  httpGET(\"/repeater\", function(req, res, err) {\n\n    text \u003c- MazamaCoreUtils::setIfNull(req$parameters$text, \"Howdy\")\n    times \u003c- MazamaCoreUtils::setIfNull(req$parameters$times, 8)\n    responseType \u003c- MazamaCoreUtils::setIfNull(req$parameters$responseType, \"txt\")\n\n    if ( times \u003e 10 )\n      stop(\"Parameter 'times' must be \u003c 10\")\n\n    res$setContentType(mime::mimemap[responseType])\n\n    if ( responseType == \"txt\" ) {\n\n      response \u003c- paste(rep(text, times), collapse = \"\\n\")\n\n    } else if ( responseType == \"json\" ) {\n\n      responseList \u003c- list(\n        status = \"success\",\n        output = paste(rep(text, times), collapse = \"\\n\")\n      )\n\n      response \u003c- jsonlite::toJSON(\n        responseList,\n        na = \"null\",\n        pretty = TRUE,\n        auto_unbox = TRUE\n      )\n\n    } else if ( responseType == \"png\" ) {\n\n      pngFile \u003c- tempfile(pattern = \"repeater\", fileext = \"png\")\n      png(pngFile)\n      plot(0:11,0:11, col = \"transparent\", axes = FALSE, xlab = \"\", ylab = \"\")\n      for ( i in 1:times ) {\n        text(1, 10 - i, text)\n      }\n      dev.off()\n      response \u003c- readr::read_file_raw(pngFile)\n\n    } else {\n\n      stop(paste0(\"responseType 'responseType' is not recognized\"))\n\n    }\n\n    return(response)\n\n  }) %\u003e%\n\n  # ----- Handle errors --------------------------------------------------------\n\n  handleErrors() %\u003e%\n\n  # ----- Start Beakr ----------------------------------------------------------\n\n  listen()\n```\n\n## Notes\n\nFundamentally, **beakr** is built on top of the **libuv** and **http-parser** C \nlibraries as beakr relies heavily upon [httpuv](https://github.com/rstudio/httpuv),\na package that provides low-level socket and protocol support for handling HTTP \nand WebSocket requests directly from within R. Much of the development of the \npackage was inspired by the excellent but no longer supported \n[jug](https://github.com/Bart6114/jug) package, developed by Bart Smeets.\n\n---- \n\nThe beakr package is supported by [Mazama Science](http://mazamascience.com/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmazamascience%2Fbeakr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmazamascience%2Fbeakr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmazamascience%2Fbeakr/lists"}