A scaffolding and deployment toolkit for building stateless web applications in R on top of the 'plumber2' web framework (< https://plumber2.posit.co/>). The UI is authored with 'bslib' and compiled to a static HTML asset at build time, while 'plumber2' serves the assets and exposes JSON API routes. Provides functions to scaffold app skeletons, run them locally, and generate Dockerfiles and images suitable for 'ShinyProxy' or plain Docker.
Shiny-style authoring, stateless API deployment.
aurora lets you build a web app the way you'd build a Shiny app β author the UI
in R with bslib, scaffold the project,
run it locally with hot-reload β but it compiles the UI to a static
index.html at build time and lets plumber2
serve that UI plus your JSON API. There's no reactive server, no per-user R
process, and no sticky sessions: the app is stateless and scales
horizontally behind a load balancer, ShinyProxy, or plain Docker.
_brand.yml; ship a static HTML asset.debian or alpine) or a ShinyProxy spec.A route handler is just an annotated function β its URL is the annotation:
# routers/iniciativas.R -> GET /api/iniciativas/data
#* @get /api/iniciativas/data
#* @serializer json
function() {
dados <- aurora_data_get(store, "iniciativas") # hot-reloaded from disk
list(data = dados, municipios = aurora_unique(dados$municipio))
}
The UI is plain bslib, compiled once to www/index.html:
# build_ui.R -> www/index.html (static, built by aurora_build_ui())
build_ui <- function() {
bslib::page_fillable(
theme = bslib::bs_theme(version = 5, brand = TRUE),
aurora_component("/api/iniciativas/data", id = "tabela") # wires a div to the endpoint
)
}
build_ui.R (bslib + _brand.yml) ββaurora_build_ui()βββΆ www/index.html (static)
Browser ββββΆ plumber2 server (aurora_app)
βββ serves www/ (static UI + core.js/app.js)
βββ routers/*.R β JSON
βββ helpers/*.R (sourced first: config, db, auth, store)
βββ optional JWT-cookie auth guard
# From GitHub β latest release (recommended):
# install.packages("pak")
pak::pak("aurora-govpe/[email protected]")
# Development version (main branch):
pak::pak("aurora-govpe/aurora-rpkg")
# From CRAN (once published):
install.packages("aurora")
remotes/devtoolswork too:remotes::install_github("aurora-govpe/[email protected]").
library(aurora)
aurora_create_app("meu_app", template = "minimal")
aurora_add_route("iniciativas", dir = "meu_app") # -> routers/iniciativas.R
aurora_run("meu_app") # http://127.0.0.1:8000 (docs at /__docs__)
aurora_run("meu_app", watch = TRUE) rebuilds the static UI on change. Theming
is a _brand.yml consumed by bslib; auth, data, and telemetry are opt-in
(template = "auth", aurora_data_store(), aurora_run(otel = TRUE)).
<app>/
βββ api.R # entry: aurora::aurora_run("."), host/port from env
βββ build_ui.R # defines build_ui() -> htmltools tag; sources ui_modules/
βββ helpers/ # *.R sourced before routers are parsed (config, db, authβ¦)
βββ routers/ # plumber2 annotated handlers; URL = annotation path
βββ ui_modules/ # ui_*.R partials
βββ www/ # static assets: js/core.js (runtime), js/app.js, style.css, images/
βββ data/config.yml # app config (config package)
βββ _brand.yml # optional: bslib theming (color/type/logo)
βββ _aurora.yml # optional: overrides name/engine/auth/packages/attach/statics
βββ Dockerfile # generated by aurora_dockerfile()
aurora_dockerfile("meu_app") # flavor = "debian" (default)
aurora_dockerfile("meu_app", flavor = "alpine") # tiny image, source-built
aurora_build_image("meu_app", tag = "org/meu_app:latest", push = TRUE)
The container serves the prebuilt static UI, so neither flavor installs
bslib/shiny at runtime. aurora_build_image() targets linux/amd64 by
default, so images built on an Apple Silicon Mac run on x86-64 servers
(pass platform = NULL for a host-native build).
debian (default) β rocker/r-ver |
alpine β rhub/r-minimal |
|
|---|---|---|
| R packages | prebuilt binaries (Posit Package Manager) | compiled from source (installr) |
| Build speed | fast | slow (full compile) |
| Image size | large (~1 GB+) | small |
| Architectures | amd64 binaries (arm64 compiles) | builds natively on amd64 and arm64 |
| System deps | apt, broad/easy (glibc) |
curated apk, must compile (musl) |
Rule of thumb: debian for fast CI and rich/geo dependencies; alpine
when image size matters most and you can absorb longer builds.
Apps that share static assets (logo, JS libraries, CSS) can serve them from one
server-side directory via _aurora.yml:
# _aurora.yml β serve /srv/aurora-shared at /assets (alongside www/ at /)
statics:
/assets: /srv/aurora-shared
Mount the directory as a volume (-v /srv/aurora-shared:/srv/aurora-shared:ro)
and every app picks up changes from one place. See vignette("deploy").
| Function | Purpose |
|---|---|
aurora_create_app() |
Scaffold the canonical layout (minimal / auth template) |
aurora_add_route() |
Generate an annotated plumber2 router in routers/ |
aurora_build_ui() / aurora_app() / aurora_run() |
Compile UI, assemble the API, run locally (incl. attach= to load _aurora.yml packages and on_exit= for shutdown cleanup) |
aurora_config() |
Read data/config.yml anchored to the app root (no cwd pitfall) |
aurora_check() |
Lint the app: UI code in runtime helpers, undeclared packages, missing UI |
aurora_component() |
Emit a UI element wired to a JSON endpoint (data-endpoint) |
aurora_unbox() / aurora_geojson() / aurora_unique() |
NULL-safe JSON response helpers (unbox / sfβGeoJSON / sorted-unique) |
aurora_data_store() |
Globals-free, hot-reloading data store for handlers |
aurora_auth_jwt() + aurora_jwt_* / aurora_*_auth_cookie() |
JWT-cookie auth scheme (pluggable) |
aurora_dockerfile() / aurora_build_image() / aurora_shinyproxy_yaml() |
Deploy: Docker + ShinyProxy |
See the Get started article, the Gallery, and the function reference.
core.js is aurora's basics; app.js and
aurora_component() keep rendering in the app, not the package.@header guard, never in aurora_app()'s core path._aurora.yml optional._brand.yml at build time.Developed by the NGR-Dados team (SEPLAG-PE) with UFPE.
MIT Β© aurora authors. Contributions welcome via issues and pull requests.