Running and deploying¶
Every app is started the same way:
if __name__ == "__main__":
app.run()
Development¶
$ python app.py --dev
Hot reload — a small supervisor restarts the app when a
*.pyfile changes (watchfilesif installed, otherwise polling). It works the same on Windows, macOS and Linux.Live browser reload — open pages reload themselves after each restart.
Readable errors — a failing page shows the exception, the line of your code with context, a hint for common mistakes and the full traceback. Errors in event handlers appear as an in-page overlay.
Binds to
127.0.0.1. Never expose dev mode: it shows source code.
--dev is the default when no mode is given, unless RATIONAL_ENV=production.
Production¶
$ python app.py --prod
$ PORT=8080 python app.py --prod --workers 1
One origin serves everything:
|
your pages |
|
|
|
runtime script, theme CSS (immutable, cache forever), events |
Production mode disables debug output, binds to $HOST (default 0.0.0.0) and
$PORT (default 8000), trusts X-Forwarded-* from $FORWARDED_ALLOW_IPS
(default 127.0.0.1) and hides the server header. Use --root-path /prefix
when a proxy serves the app under a sub-path.
Important
Page views (component state) live in process memory. Run one worker per container,
or use sticky sessions when you run several (--workers N prints a reminder).
Static export¶
A site made of static pages does not need a running server. Export it as plain HTML files and host them anywhere (GitHub Pages, Netlify, S3, a CDN):
$ python app.py --export site/
$ python app.py --export site/ --root-path /my-repo # served under a sub-path
This writes site/index.html, site/<path>/index.html, site/_rational/theme.css,
a 404.html and a copy of every directory mounted with app.static. Only pages that
work without a server are exported: those with no path parameters, no auth="required"
and no interactivity (any page that loads the browser runtime, such as one with event handlers).
Everything else is listed as skipped with the reason, and the command exits non-zero if nothing could
be exported. The same export is available from code as rational.export.export_site().
Request logging and tracing¶
Every HTTP response includes an X-Request-ID header. A valid ID supplied by the
caller is preserved; otherwise Rational generates one. The rational standard-library
logger emits structured LogRecord fields for request completion and event dispatch,
including the request ID and elapsed milliseconds. Slow page renders are logged at
WARNING; the default threshold is 100 ms.
app.logging.configure(
request_id_header="X-Request-ID",
slow_render_threshold_ms=75,
)
To emit OpenTelemetry spans for requests, page renders and event dispatch, install
rational[telemetry], configure an OpenTelemetry SDK provider/exporter in the host
application, then enable tracing:
app.logging.configure(opentelemetry=True)
The framework does not install or configure an exporter, so the host application retains control over where telemetry is sent.
Other deployment strategies¶
- Any ASGI server
Appis an ASGI application:uvicorn app:app --host 0.0.0.0 --port 8000 --proxy-headers gunicorn app:app -k uvicorn.workers.UvicornWorker -w 1
- The module entry point
Run an app without a
__main__block, or with an import string:python -m rational path/to/app.py --prod python -m rational mypackage.web:app --prod --workers 2
- Inside an existing FastAPI application
api = FastAPI() api.mount("/app", rational_app) # or add routes to rational_app.fastapi
- Behind a reverse proxy / CDN
Terminate TLS at the proxy, forward
X-Forwarded-Protoso cookies getSecure, and cache/_rational/*.js|cssaggressively (their URLs are content-hashed).- Containers
FROM python:3.13-slim WORKDIR /srv COPY . . RUN pip install --no-cache-dir "rational[db]" . ENV RATIONAL_ENV=production CMD ["python", "app.py", "--prod"]
- Content Security Policy
App(csp=True)sends a strict policy with a per-response nonce. No inlinestyle=attributes or inline scripts are generated.