{"id":13401307,"url":"https://github.com/thomasp85/routr","last_synced_at":"2025-04-04T20:16:19.649Z","repository":{"id":66336328,"uuid":"60770171","full_name":"thomasp85/routr","owner":"thomasp85","description":"Routing of Web Requests in R","archived":false,"fork":false,"pushed_at":"2025-03-28T12:26:18.000Z","size":3901,"stargazers_count":55,"open_issues_count":2,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-28T13:29:47.488Z","etag":null,"topics":["http-router","middleware","router","rstats","webtechnology"],"latest_commit_sha":null,"homepage":"https://routr.data-imaginist.com","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomasp85.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-06-09T11:35:35.000Z","updated_at":"2025-03-28T12:23:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"2b4f1941-b37f-4ae7-88ae-6a024b5baebc","html_url":"https://github.com/thomasp85/routr","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasp85%2Froutr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasp85%2Froutr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasp85%2Froutr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasp85%2Froutr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomasp85","download_url":"https://codeload.github.com/thomasp85/routr/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247242683,"owners_count":20907134,"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":["http-router","middleware","router","rstats","webtechnology"],"created_at":"2024-07-30T19:01:01.217Z","updated_at":"2025-04-04T20:16:19.630Z","avatar_url":"https://github.com/thomasp85.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n# routr \u003cimg src=\"man/figures/logo.png\" height=\"140px\" align=\"right\"/\u003e\n\n\u003c!-- badges: start --\u003e\n[![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable)\n[![R-CMD-check](https://github.com/thomasp85/routr/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/thomasp85/routr/actions/workflows/R-CMD-check.yaml)\n[![CRAN status](https://www.r-pkg.org/badges/version/routr)](https://CRAN.R-project.org/package=routr)\n[![Codecov test coverage](https://codecov.io/gh/thomasp85/routr/graph/badge.svg)](https://app.codecov.io/gh/thomasp85/routr)\n\u003c!-- badges: end --\u003e\n\n```{r, echo = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"README-\"\n)\nlibrary(routr)\n```\n\nroutr is a simple and versatile router for R based web servers. For people not\nfamiliar with back-end development, a router is a piece of middleware that\ndelegates HTTP requests to the correct handler function. The delegation is based\nin the URL of the request and in essence means that requests directed at\n*/persons/thomas/* ends up in another handler than */packages/routr/*.\n\nroutr is heavily inspired by other routers build for other platforms, especially\nthose for [Express.js](https://github.com/expressjs) and\n[Ruby on Rails](https://github.com/rails/rails), though it doesn't mimick\neither.\n\n## Installation\n`routr` is available on CRAN and can be installed in the regular way.\n\n```{r, eval=FALSE}\ninstall.packages('routr')\n```\n\nAlternatively you can grab the development version from Github with\n\n```{r, eval=FALSE}\n# install.packages('devtools')\ndevtools::install_github('thomasp85/routr')\n```\n\n## Functionality\nA router is build up of several seperate routes that are\ncollected in a route stack. The stack recieves the request and passes it on to\nthe first route in the stack. Depending on whether the route can handle the\nrequest and whether the handler signals a fall-through, the request is passed\nalong the stack until a handler signals that no further processing should be\ndone. This means that it is possible to stack different functionality like user\nverification, static ressource serving, etc. on top of each other.\n\n### The handler\nA handler is a function that accepts the arguments `request`, `response`,\n`keys`, and `...`. The handler must return a boolean indicating if the request\nshould be passed down the stack (`TRUE`) or not (`FALSE`). `routr` uses the\n[`reqres`](https://github.com/thomasp85/reqres#reqres) package to provide\npowerful request and response classes that makes it easy to work with an HTTP\nexchange. An example of a simple handler is:\n\n```{r}\nh \u003c- function(request, response, keys, ...) {\n    response$status \u003c- 200L\n    response$type \u003c- 'html'\n    response$body \u003c- '\u003ch1\u003eHello World!\u003c/h1\u003e'\n    return(FALSE)\n}\n```\n\nNo matter the content of the request passed to this handler it will return a\n\"Hello World!\" to the client. Because it returns `FALSE` it block any other\nhandlers below it to modify the response.\n\n### The route\nA route is a collection of handlers. For any given request, only one handler in\nthe route will be called. A route is an object of the R6 Route class and can be\ncreated as so:\n\n```{r}\nroute \u003c- Route$new()\nroute$add_handler('get', '/hello/:what/', h)\n```\n\nThe first argument to `add_handler` defines the [request type](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods)\nwhile the second defines the path that the handler responds to. The path need\nnot be static. In the above example the `:what` defines a variable meaning that\nthe handler will respond to any `/hello/\u003csomething\u003e/` variation. The variable\nand the value is available to the handler in the keys argument. For instance, if\na request with the URL `/hello/mars/` were passed through the route, the keys\nargument passed to the handler would contain `list(what = 'mars')`. Variables\ncan only span a single level, meaning that the above handler would not respond\nto `/hello/jupiter/saturn/`. To match to anything use `/hello/*` for responding\nto any sub-URL to `hello`. Matches to `*` will not end up in the keys list. If\nseveral paths in a route matches a URL the most specific will be used, meaning\nthat `/*` will match everything but will always chosen last. With all that in mind\nlets change the handler to respond to the `what` variable:\n\n```{r}\nh \u003c- function(request, response, keys, ...) {\n    response$status \u003c- 200L\n    response$type \u003c- 'html'\n    response$body \u003c- paste0('\u003ch1\u003eHello ', keys$what, '!\u003c/h1\u003e')\n    return(FALSE)\n}\nroute$add_handler('get', '/hello/:what/', h)\n```\n\nLet's also add a fallback handler that captures everything:\n\n```{r}\nhFallback \u003c- function(request, response, keys, ...) {\n    response$status \u003c- 200L\n    response$type \u003c- 'html'\n    response$body \u003c- '\u003ch1\u003eI\\'m not saying hello to you\u003c/h1\u003e'\n    return(FALSE)\n}\nroute$add_handler('get', '/*', hFallback)\n```\n\n### The route stack\nThe route stack manages several routes and takes care of receiving a request and\nreturning a response. A route stack is an object of the R6 class `RouteStack`\nand is created like this:\n\n```{r}\nrouter \u003c- RouteStack$new()\nrouter$add_route(route, 'test')\n```\n\nThe order in which routes are added to the stack determines the calling order,\nwith those added first taking precedence over those added later. Request are\nhandled by the `dispatch` method like so:\n\n```{r, eval=FALSE}\nrouter$dispatch(request)\n```\n\n### Use with fiery\nA `RouteStack` is a [fiery](https://github.com/thomasp85/fiery)-compliant plugin\nmeaning that it can be passed to the `attach()` method of a fiery server. This\nwill set the server up to pass requests through the route stack and use the\nresulting response automatically\n\n```{r, eval=FALSE}\napp \u003c- fiery::Fire$new()\napp$attach(router)\napp$ignite(block = FALSE)\n# In Terminal (or visit in browser)\n# curl http://127.0.0.1:8080/hello/mars/\n# \u003ch1\u003eHello Mars!\u003c/h1\u003e\napp$extinguish()\n```\n\nBy default the router responds to `request` events but can also be used to\ndispatch on `header` and `message` events. In the latter case the request that\nis send through the handlers is a modified version of the request used to\nestablish the WebSocket version. If used as a WebSocket router a way to extract\nthe path to dispatch on must be provided as part of the `RouteStack`\nconstruction.\n\n## Code of Conduct\nPlease note that the 'routr' project is released with a\n[Contributor Code of Conduct](https://routr.data-imaginist.com/CODE_OF_CONDUCT.html).\nBy contributing to this project, you agree to abide by its terms.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasp85%2Froutr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomasp85%2Froutr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasp85%2Froutr/lists"}