MyNixOS website logo
Description

Build Stateless Web Apps with 'plumber2'.

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.

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

  • 🎨 UI in R β€” author with bslib + _brand.yml; ship a static HTML asset.
  • ⚑ Stateless β€” plumber2 serves the assets and JSON routes; scale by adding containers.
  • 🧩 Convention over config β€” fixed folders, no required manifest.
  • πŸ” Batteries, opt-in β€” JWT-cookie auth, a hot-reloading data store, OpenTelemetry.
  • 🐳 One-command deploy β€” generate a Dockerfile (debian or alpine) or a ShinyProxy spec.

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/[email protected]")

# 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/[email protected]").

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-veralpine β€” rhub/r-minimal
R packagesprebuilt binaries (Posit Package Manager)compiled from source (installr)
Build speedfastslow (full compile)
Image sizelarge (~1 GB+)small
Architecturesamd64 binaries (arm64 compiles)builds natively on amd64 and arm64
System depsapt, 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

FunctionPurpose
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

  • plumber2 only. Each handler's URL comes from its annotation.
  • Thin JS runtime.core.js is aurora's basics; app.js and aurora_component() keep rendering in the app, not the package.
  • Pluggable auth. A JWT-cookie @header guard, never in aurora_app()'s core path.
  • Convention over manifest. Fixed folders; _aurora.yml optional.
  • Theming via brand.yml. bslib consumes _brand.yml at build time.

Authors

  • Andre Leite β€” maintainer ([email protected])
  • Marcos Wasilew Β· Hugo Vasconcelos Β· Carlos Amorin Β· Diogo Bezerra Β· JΓΊlia Nascimento Barreto

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

License

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

Metadata

Version

0.1.12

License

Unknown

PlatformsΒ (79)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    wasip1
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasip1
  • wasm64-wasip1
  • x86_64-cygwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows