{"id":50987054,"url":"https://github.com/pachadotdev/openapi","last_synced_at":"2026-06-19T20:31:26.490Z","repository":{"id":360065793,"uuid":"1223767415","full_name":"pachadotdev/openapi","owner":"pachadotdev","description":"An OpenAPI Specification (OAS) compliant REST API framework for R inspired by Python's Flask.","archived":false,"fork":false,"pushed_at":"2026-04-28T16:28:03.000Z","size":453,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T21:32:11.605Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pachadotdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-28T16:27:12.000Z","updated_at":"2026-04-29T18:54:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pachadotdev/openapi","commit_stats":null,"previous_names":["pachadotdev/openapi"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/pachadotdev/openapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pachadotdev%2Fopenapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pachadotdev%2Fopenapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pachadotdev%2Fopenapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pachadotdev%2Fopenapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pachadotdev","download_url":"https://codeload.github.com/pachadotdev/openapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pachadotdev%2Fopenapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34547787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":"2026-06-19T20:31:25.682Z","updated_at":"2026-06-19T20:31:26.477Z","avatar_url":"https://github.com/pachadotdev.png","language":"R","funding_links":["https://www.buymeacoffee.com/pacha"],"categories":[],"sub_categories":[],"readme":"# openapi\n\n[![Test coverage](https://raw.githubusercontent.com/pachadotdev/openapi/coverage/badges/coverage.svg)](https://github.com/USER/REPO/actions/workflows/test-coverage.yaml)\n[![BuyMeACoffee](https://raw.githubusercontent.com/pachadotdev/buymeacoffee-badges/main/bmc-donate-white.svg)](https://www.buymeacoffee.com/pacha)\n\nAn OpenAPI Specification (OAS) compliant REST API framework for R inspired by Python's Flask.\n\nThis is designed to run as a service (e.g., via systemctl on Linux). Everything is returned as\nJSON and this package allows to return data from SQL connections or any type of object that can\nbe serialized as JSON.\n\n## Features\n\n- OpenAPI 3.0 specification auto-generation\n- Built-in Swagger UI at `/docs`\n- OpenAPI spec served at `/openapi.json`\n- Flask-like routing with piping support\n- Automatic parameter extraction from query strings\n\n## Installation\n\n```r\nremotes::install_github(\"pachadotdev/openapi\")\n```\n\n## Simple example with piping\n\nPaste this into an R console\n\n```r\nlibrary(openapi)\n\napi \u003c- api_init(\n  title = \"My API\",\n  version = \"1.0.0\",\n  description = \"A sample API built with openapi for R\"\n) |\u003e\n  api_get(\"/\", function() {\n    list(message = \"Welcome to openapi for R!\")\n  },\n  summary = \"Root endpoint\",\n  description = \"Returns a welcome message\"\n  ) |\u003e\n  api_get(\"/hello\", function() {\n    list(greeting = \"Hello World!\")\n  },\n  summary = \"Hello endpoint\",\n  tags = c(\"greetings\")\n  ) |\u003e\n  api_get(\"/greet\", function(name) {\n    if (is.na(name)) name \u003c- \"Moto\"\n    list(greeting = paste(\"Hello,\", name, \"!\"))\n  },\n  summary = \"Greet by name\",\n  description = \"Greets the user by name, defaults to 'Moto' if not provided\",\n  tags = c(\"greetings\")\n  )\n\n# Run the server\napi_run(api, host = \"127.0.0.1\", port = 5000, debug = TRUE)\n```\n\nthen visit\n\n- http://127.0.0.1:5000/ - Your API\n- http://127.0.0.1:5000/docs - Swagger UI documentation\n- http://127.0.0.1:5000/openapi.json - OpenAPI specification\n- http://127.0.0.1:5000/hello\n- http://127.0.0.1:5000/greet?name=World\n\n## Example without piping\n\nUndefined parameters are `NA`.\n\n```r\nlibrary(openapi)\n\napi \u003c- api_init()\n\napi \u003c- api_get(api, \"/mtcars1\", function(cyl, am) {\n    # sanitize parameters\n    cyl \u003c- as.integer(substr(cyl, 1, 1))\n    am \u003c- as.integer(substr(am, 1, 1))\n\n    d \u003c- mtcars\n    \n    if (!is.na(cyl) \u0026\u0026 cyl \u003e 0) { d \u003c- d[d$cyl == cyl, ] }\n    if (!is.na(am) \u0026\u0026 am %in% 0:1) { d \u003c- d[d$am == am, ] }\n\n    d\n})\n\napi \u003c- api_get(api, \"/mtcars2\", function(cyl, vs) {\n    # sanitize parameters\n    cyl \u003c- as.integer(substr(cyl, 1, 1))\n    vs \u003c- as.integer(substr(vs, 1, 1))\n\n    d \u003c- mtcars\n    \n    if (!is.na(cyl) \u0026\u0026 cyl \u003e 0) { d \u003c- d[d$cyl == cyl, ] }\n    if (!is.na(vs) \u0026\u0026 vs %in% 0:1) { d \u003c- d[d$vs == vs, ] }\n\n    d\n})\n\napi_run(api, host = \"127.0.0.1\", port = 5000, debug = TRUE)\n```\n\nthen visit\n\nhttp://127.0.0.1:5000/mtcars1?cyl=6\nhttp://127.0.0.1:5000/mtcars1?cyl=4\u0026am=0\netc.\n\n## OpenAPI Specification\n\nThe package automatically generates an OpenAPI 3.0 specification from your routes.\n\n### Accessing the spec\n\nWhen the server is running:\n- Swagger UI: http://127.0.0.1:5000/docs\n- OpenAPI JSON: http://127.0.0.1:5000/openapi.json\n\n### Exporting the spec\n\n```r\n# Get spec as an R list\nspec \u003c- api_spec(api, host = \"127.0.0.1\", port = 5000)\n\n# Write spec to file\napi_spec_write(api, path = \"openapi.json\", host = \"127.0.0.1\", port = 5000)\n```\n\n### Adding metadata to routes\n\n```r\napi \u003c- api_init(\n  title = \"My API\",\n  version = \"2.0.0\",\n  description = \"API description here\"\n) |\u003e\n  api_get(\n    \"/users\",\n    function() { list(users = c(\"alice\", \"bob\")) },\n    summary = \"List all users\",\n    description = \"Returns a list of all registered users\",\n    tags = c(\"users\")\n  ) |\u003e\n  api_post(\n    \"/users\",\n    function(name) { list(created = name) },\n    summary = \"Create a user\",\n    tags = c(\"users\")\n  )\n```\n\n## Running the server\n\n### Blocking (for production)\n\nRuns until Ctrl+C or that you interrupt the API service (see the systemctl example below)\n\n```r\napi_run(api, host = \"0.0.0.0\", port = 5000)\n```\n\n### Non-blocking (for development/testing)\n\n```r\n# returns modified api, so you must reassign\napi \u003c- api_run_background(api, port = 5000)\n\n# do other things\nhttpuv::service()  # process requests while doing other work\n\n# stop when done\napi_stop(api)\n```\n\n## Response formats\n\nBy default, return values are JSON-serialized (lists and data frames work automatically):\n\n```r\n# Error response with custom status\napi \u003c- api |\u003e\n  api_get(\"/fail\", function() {\n    response_error(\"Something went wrong\", status = 400)\n  })\n```\n\n## Migrating from plumber v1\n\n### Before (plumber)\n\n```r\n#* @get /hello\nfunction() {\n  list(message = \"Hello!\")\n}\n\n#* @param name The name to greet\n#* @get /greet\nfunction(name = \"World\") {\n  list(greeting = paste(\"Hello,\", name))\n}\n```\n\n### After (openapi)\n\n```r\napi \u003c- api_init() |\u003e\n  api_get(\"/hello\", function() {\n    list(message = \"Hello!\")\n  }) |\u003e\n  api_get(\"/greet\", function(name) {\n    if (is.na(name)) name \u003c- \"World\"\n    list(greeting = paste(\"Hello,\", name))\n  })\n\napi_run(api)\n```\n\n## systemd service\n\nCreate `/etc/systemd/system/openapi-api.service` or similar:\n\n```ini\n[Unit]\nDescription=OpenAPI R API\nAfter=network.target\n\n[Service]\nType=simple\nUser=www-data\nWorkingDirectory=/path/to/api\nExecStart=/usr/bin/Rscript api.R\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n```\n\nThen:\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable openapi-api\nsudo systemctl start openapi-api\n```\n\nTo restart/stop the API:\n\n```bash\nsudo systemctl restart openapi-api\nsudo systemctl stop openapi-api\n```\n\n## License\n\nApache Licence 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpachadotdev%2Fopenapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpachadotdev%2Fopenapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpachadotdev%2Fopenapi/lists"}