Running your app¶
A Golit blueprint (App) isn't itself a web server — you turn it into an ASGI application and serve that. There are two ways, and they meet in the middle.
create_app¶
create_app(app) wires your blueprint into a runnable Litestar ASGI application:
This is the conventional last line of an app module. The resulting application is a standard ASGI app — any ASGI server can serve it:
create_app takes a few keyword options for advanced use:
pubsuboverrides the SSE fan-out backend (default: chosen from the environment — Redis whenGOLIT_REDIS_URLis set, in-memory otherwise). See Server-push updates.on_startup/on_shutdownadd lifecycle hooks — e.g. to launch a background ticker that pushes updates.
golit run¶
The CLI is the quickest path during development:
It loads the file and launches it under Uvicorn. The file may expose either:
- a Litestar
application(e.g.application = create_app(app)), or - a bare Golit
app(anApp), whichgolit runwraps withcreate_appautomatically.
So even this minimal module runs:
from golit import App, slider
app = App(title="Minimal")
@app.view
def out(n: int = slider(0, 10)) -> str:
return f"<p>{n}</p>"
# no create_app needed — `golit run` finds `app`
Options¶
| Flag | Default | Meaning |
|---|---|---|
--host |
127.0.0.1 |
Bind address. |
--port |
8000 |
Port. |
--workers |
1 |
Worker processes. |
--workers > 1 is for local testing, not production
Golit keeps session state worker-local (it's what makes recompute cheap). Uvicorn's --workers share one socket with no session affinity, so a returning client can land on a worker that doesn't hold its state. golit run --workers N prints a warning and exists for convenience. The production path is N single-worker instances behind a sticky load balancer + Redis — see Deployment & scaling.
python -m golit¶
golit and python -m golit are equivalent entry points:
Splitting across modules¶
A one-file app.py is fine to start, but a real app spreads its nodes across files. The key
fact: @app.source / @app.reactive / @app.view register on the app instance when the
decorator runs — at import. So two rules are all you need:
- One shared
app. Putapp = App(...)in its own module and have every node modulefrom myapp import app. Python caches modules, so they all decorate the same instance. - Import every node module before serving. The entrypoint imports them for their
side-effects, then calls
create_app(app). Golit resolves the graph across all of them — a view inviews.pycan depend on a reactive inreactives.pyby name; files don't matter.
import reactives, sources, views # noqa: F401 — importing registers their @app.* nodes
from myapp import app
from golit import create_app
application = create_app(app)
golit run app.py executes that one file on its own (not as a package), so when the modules
are plain siblings, put their folder on sys.path first:
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent)) # then `import sources, reactives, views`
For an installable project, prefer a real package with relative imports and serve the
import string directly — no sys.path line needed:
myapp/
__init__.py
app.py # app = App(...)
sources.py # from .app import app
reactives.py # "
views.py # "
main.py # from . import sources, reactives, views; application = create_app(app)
The runnable examples/modular
shows the golit run layout end to end.
The routes it serves¶
Once running, your app exposes three endpoints (you rarely call them directly — HTMX does):
| Route | Purpose |
|---|---|
GET / |
The full page: controls + every view, for a session. |
POST /node/{input_id} |
Commit an input change; returns the changed view fragments as out-of-band swaps. |
GET /events |
The SSE push channel: one long-lived stream per session. |
How these fit together is the subject of How a change flows.
Next¶
You've finished the tutorial. Two good directions: