# asgi-autoreload

Development middleware for Python ASGI frameworks that automatically reloads the browser when files change.

It works by injecting a small SSE client script into every HTML response. When a watched file changes, the server pushes a `reload` event and the browser refreshes — no manual F5 needed.

Compatible with **Django**, **FastAPI**, **Starlette**, and **Litestar**.

---

## Installation

```bash
# Django (via Daphne)
pip install asgi-autoreload[django]

# FastAPI
pip install asgi-autoreload[fastapi]

# Starlette
pip install asgi-autoreload[starlette]

# Litestar
pip install asgi-autoreload[litestar]
```

---

## Usage

### FastAPI

Use the `fastapi-autoreload` CLI as a drop-in replacement for `fastapi dev`. It accepts all the same options, plus:

- `--watch` — paths to monitor (default: `.`)
- `-- <command>` — build command to run on startup and on each file change

```bash
fastapi-autoreload dev main.py
fastapi-autoreload dev main.py --watch src --watch templates
fastapi-autoreload dev main.py --host 0.0.0.0 --port 8080

# Run a build step before each browser reload
fastapi-autoreload dev main.py -- npm run build
fastapi-autoreload dev main.py --watch src -- npx tailwindcss -i src/input.css -o static/output.css
```

See [`examples/fastapi/`](examples/fastapi/) for a complete working example.

### Django

Add `asgi_autoreload.django` to `INSTALLED_APPS`, then use the custom management command instead of `runserver`:

```python
# settings.py
INSTALLED_APPS = [
    ...
    "asgi_autoreload.django",
]
```

```bash
python manage.py autoreload_runserver
python manage.py autoreload_runserver --watch src --watch templates

# Run a build step before each browser reload
python manage.py autoreload_runserver -- npm run build
python manage.py autoreload_runserver --watch src -- npx tailwindcss -i src/input.css -o static/output.css
```

This wraps Daphne's ASGI server with the autoreload middleware. Make sure your project is configured with an ASGI application (`ASGI_APPLICATION` in settings).

See [`examples/django/`](examples/django/) for a complete working example.

### Starlette

Wrap your application manually with `AutoreloadMiddleware`:

```python
from starlette.applications import Starlette
from asgi_autoreload import AutoreloadMiddleware

app = Starlette(...)
app = AutoreloadMiddleware(
    app,
    watch_paths=["src", "templates"],
    build_command=["npm", "run", "build"],  # optional
)
```

```bash
uvicorn main:app
```

### Litestar

Same approach as Starlette — wrap the app directly:

```python
from litestar import Litestar
from asgi_autoreload import AutoreloadMiddleware

app = Litestar([...])
app = AutoreloadMiddleware(
    app,
    watch_paths=["src", "templates"],
    build_command=["npm", "run", "build"],  # optional
)
```

```bash
uvicorn main:app
```

See [`examples/litestar/`](examples/litestar/) for a complete working example.

---

## How it works

- **HTML injection** — a `<script>` tag opening an `EventSource('/_autoreload')` connection is injected before `</body>` in every `text/html` response. Compressed responses (`Content-Encoding: gzip`, etc.) are left untouched.
- **SSE endpoint** — `/_autoreload` is handled by the middleware itself and streams `reload` events to connected browsers.
- **File watcher** — [`watchfiles`](https://watchfiles.helpmanual.io/) monitors the specified paths and triggers a broadcast on any change.
- **Build command** — if provided, the command runs once on startup and again before each browser reload, so assets are always up to date when the page refreshes.
- **ASGI lifecycle** — the watcher starts on `lifespan.startup` and stops cleanly on `lifespan.shutdown`. If lifespan is not supported by the server, it starts lazily on the first request.
