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/aurora-rpkg@v0.1.5")
# 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/aurora-rpkg@v0.1.5").
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-sharedMount 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.