aurora aurora logo

R-CMD-check Docs License: MIT

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.

What an app looks like

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

Installation

# 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/devtools work too: remotes::install_github("aurora-govpe/aurora-rpkg@v0.1.5").

Quick start

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)).

Canonical app layout (convention; no manifest required)

<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()

Deploy

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").

Core functions

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.

Design decisions

Authors

Developed by the NGR-Dados team (SEPLAG-PE) with UFPE.

License

MIT Β© aurora authors. Contributions welcome via issues and pull requests.