What aurora is
aurora builds stateless web apps on top of plumber2: you author the UI with bslib, aurora compiles it to
a static index.html at build time, and plumber2 serves
those assets plus your JSON API routes. You get Shiny-like ergonomics
with an API deployment model — no WebSocket, no per-user R process, no
sticky sessions. R runs only when a route is called, so an app scales
horizontally behind a load balancer or Docker.
Scaffold, run, add a route
library(aurora)
aurora_create_app("meu_app", template = "minimal")
aurora_run("meu_app") # http://127.0.0.1:8000 (docs at /__docs__)
# add an API route (writes routers/relatorios.R with the path in the annotation)
aurora_add_route("relatorios", dir = "meu_app")During development, aurora_run("meu_app", watch = TRUE)
rebuilds the static UI whenever build_ui.R or
ui_modules/ change — just refresh the browser.
The app contract (convention, not a manifest)
aurora assembles an app from a fixed layout. Nothing here is required to be wired by hand; aurora finds it by convention.
meu_app/
├── api.R # entry point: aurora::aurora_run("."), host/port from env
├── build_ui.R # defines build_ui() -> an htmltools tag
├── helpers/ # *.R sourced BEFORE routers are parsed (db, config, stores)
├── routers/ # plumber2 annotated handlers; the URL is the annotation path
├── ui_modules/ # ui_*.R partials sourced by build_ui.R
├── www/ # static assets: js/core.js (runtime), js/app.js, style.css, images/
├── data/config.yml # config (the config package)
├── _brand.yml # optional: visual brand consumed by bslib
└── _aurora.yml # optional: overrides name / engine / auth / statics
Writing a route
A handler’s URL comes from its annotation. Note plumber2 only binds
path parameters to named arguments; read the query
string from the query argument and a parsed body from
body (see
vignette("migrating-from-shiny")).
Wiring UI to the API
aurora ships a thin runtime (www/js/core.js) exposing
window.aurora: a credentialed
fetch/json wrapper plus an
onUnauthorized hook. It ships no rendering
code — your app.js renders. Use
aurora_component() to emit an element wired to its
endpoint, then read it from JS:
# build_ui.R
aurora_component("api/sales/data", id = "chart", style = "height:360px;")// www/js/app.js
var el = document.getElementById("chart");
aurora.json(el.dataset.endpoint).then(function (d) {
echarts.init(el).setOption({
xAxis: { type: "category", data: d.categories },
yAxis: { type: "value" },
series: [{ type: "bar", data: d.values }]
});
});See inst/examples/02-dashboard-echarts and
03-map for full worked apps.
Theming, data, auth, telemetry
-
Theming — edit
_brand.yml; bslib bakes it in at build time. Theminimaltemplate usesbs_theme(version = 5, brand = TRUE). -
Data —
aurora_data_store()hands handlers a hot-reloading, globals-free store:aurora_data_get(store, "sales")re-reads when the file changes on disk. -
Auth — opt-in JWT-cookie scheme; scaffold it with
aurora_create_app(..., template = "auth"). Seevignette("auth"). -
Telemetry —
aurora_run(otel = TRUE). Seevignette("telemetry"). -
Shared assets — serve a directory shared across
apps at its own URL prefix with
_aurora.yml’sstatics:(e.g.statics:then/assets: /srv/shared). Seevignette("deploy").
Deploy
aurora_dockerfile("meu_app")
aurora_build_image("meu_app", tag = "org/meu_app:latest", push = TRUE)See vignette("deploy").