vyuh 0.2.8

Vyuh web framework for Axum and SQLx with handler-first APIs
Documentation
# Vyuh

[![Crates.io](https://img.shields.io/crates/v/vyuh)](https://crates.io/crates/vyuh)
[![docs.rs](https://img.shields.io/docsrs/vyuh)](https://docs.rs/vyuh)
[![License](https://img.shields.io/crates/l/vyuh)](LICENSE)

_Vyuh_ (व्यूह, _vyoo-huh_) means "formation", "arrangement", or "structured
configuration".

Vyuh is a handler-first Rust web framework built on Axum and SQLx.

It is designed around one idea: application structure should stay visible in
ordinary Rust code. Handler signatures describe what a route, command, task, or
signal consumes. Bundles group related application parts. `Site` owns the
runtime. `Data<T>` gives typed application data the same shape across
subsystems.

Vyuh favors a narrow, explicit API over hidden framework magic. Request wrappers
parse. `Valid<E>` validates. Auth is opt-in. Tasks retry only when the task says
so. Services stay separate because they are site-lifetime components, not
handler data.

Vyuh is usable, but not API-stable yet. Expect breaking changes before a stable
release.

## Web And Docs Assets

The `vyuh/web/` directory is the shared visual source for Vyuh-owned web
surfaces. It contains public CSS, JavaScript, images, the static landing-page
source, and private Minijinja templates for the built-in console.

The mdBook source lives in `docs/book/` and reuses the same CSS:

```sh
mdbook build docs/book
```

The landing page and mdBook can be built into one GitHub Pages artifact:

```sh
scripts/build-pages.sh
```

For the `vyuh-rs/vyuh` repository, GitHub Pages will serve the landing page at
`https://vyuh-rs.github.io/` and the book at
`https://vyuh-rs.github.io/docs/`.

The console UI is server-rendered with Minijinja and uses the same bundled asset
serving path as application assets, for example `/assets/css/base.css` and
`/assets/css/console.css`.

## The Shape

Vyuh tries to make the common application path cohesive:

- `Data<T>` is typed application data.
- `Valid<E>` opts into validation.
- `Site` is the runtime handle and lifecycle surface.
- Bundles are the composition unit for features.
- Handler signatures drive extraction, auth, validation, OpenAPI, and subsystem
  access.
- Macros are convenience syntax. The same routes, commands, tasks, signals,
  emitters, services, assets, and OpenAPI wiring can be registered with the
  direct API.

The goal is not to hide the framework. The goal is to keep each framework
concept small, explicit, and in the same place as the code that needs it.

## Getting Started

```rust
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use vyuh::prelude::*;

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, Validate)]
struct Signup {
    #[validate(email)]
    email: String,

    #[validate(min_length = 3, max_length = 80)]
    name: String,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
struct UserCreated {
    id: i64,
    email: String,
    name: String,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
struct SystemPulse {
    project: String,
}

#[bundles::route(path = "/users", method = "POST")]
async fn signup(Valid(Data(input)): Valid<Data<Signup>>) -> Result<Data<UserCreated>, Error> {
    Ok(Data::new(UserCreated {
        id: 1,
        email: input.email.clone(),
        name: input.name.clone(),
    }))
}

#[bundles::cron(expr = "0 */5 * * * *")]
async fn heartbeat(site: Site) -> Data<SystemPulse> {
    Data::new(SystemPulse {
        project: site.project_dir().display().to_string(),
    })
}

#[bundles::signal]
async fn record_heartbeat(Data(pulse): Data<SystemPulse>) {
    println!("heartbeat for {}", pulse.project);
}

#[tokio::main]
async fn main() -> Result<(), vyuh::SiteError> {
    let app = bundles::bundle! {
        signup,
        heartbeat,
        record_heartbeat,
    }
    .with_openapi(
        bundles::OpenApiConf::default()
            .title("Vyuh Example")
            .version("0.1.0")
            .description("Routes, typed data, emitters, signals, and OpenAPI.")
            .spec("/openapi.json"),
    )
    .with_prefix("/api");

    Site::run(SiteConf::from_env_with_files()?, app).await
}
```

This bundle registers a validated JSON route, a cron emitter, a signal handler,
and an OpenAPI spec endpoint. The route parses and validates `Data<Signup>`,
returns `Data<UserCreated>` as JSON, and contributes request, response, and
validation metadata to OpenAPI from the handler signature. Without `Valid`,
`Data<Signup>` would parse only.

`Site::run` is the normal application entrypoint. It builds the site, runs the
requested command, and defaults to serving HTTP when no command is supplied. Use
`Site::serve` for server-only binaries and `Site::build` when embedding or
testing needs the built site object.

## Bundles

A bundle is Vyuh's feature composition unit.

A feature can own its routes, templates, assets, services, tasks, commands,
signals, emitters, and OpenAPI metadata together. Larger applications are built
by merging and prefixing bundles instead of scattering feature wiring across
unrelated global registries.

```rust
let dashboard = bundles::bundle! {
    dashboard_routes,
    dashboard_service,
    rebuild_dashboard_index,
    dashboard_assets,
};

let app = bundles::bundle! {
    public_routes,
    auth_routes,
    dashboard.with_prefix("/dashboard"),
};
```

## Philosophy

Vyuh is built around simplicity and uniformity:

- One typed data wrapper, `Data<T>`, is shared across routes, commands, tasks,
  signals, and emitters.
- Validation is explicit through `Valid<E>`, never automatic because a type
  derives `Validate`.
- Auth is completely opt-in; routes without auth extractors do no auth work.
- Errors have a common application shape, then render differently for HTTP,
  commands, and tasks.
- Site-level subsystems are accessed through direct handles such as `site.db()`,
  `site.tasks()`, `site.templates()`, `site.file_storage()`, and `site.auth()`.
- Services remain distinct because they represent site-lifetime components, not
  handler input or output.
- Console is opt-in and read-only in its first pass, for operational inspection
  without exposing command or task mutation APIs.

This keeps the framework cohesive without pretending every subsystem is the
same thing.

## Documentation

- [Site]docs/site.md: lifecycle, configuration, subsystem handles, and
  testing.
- [Bundles]docs/bundles.md: feature composition, prefixing, OpenAPI, and
  validation.
- [Routes]docs/routes.md: handler-first HTTP routes and route metadata.
- [Request]docs/request.md: `Data`, JSON, query, path, forms, multipart, and
  raw bodies.
- [Response]docs/response.md: response wrappers, redirects, headers, and
  metadata.
- [Validation]docs/validation.md: explicit `Valid<E>`, structured errors, and
  schema hints.
- [Errors]docs/errors.md: application errors, rendered errors, commands, and
  tasks.
- [Auth]docs/auth.md: opt-in JWT, API keys, static roles, dynamic
  permissions, and Django password hashes.
- [OpenAPI]docs/openapi.md: generated specs from handler signatures and
  overrides.
- [Middlewares]docs/middlewares.md: site-wide HTTP policy and slash behavior.
- [Database]docs/db.md: SQLx access, query helpers, sessions, and
  transactions.
- [Tasks]docs/tasks.md: durable single-unit background continuations.
- [Commands]docs/commands.md: site-aware CLI commands for operations.
- [Services]docs/services.md: site-lifetime components and workers.
- [Templates]docs/templates.md: Minijinja configuration, helpers, and
  formatting.
- [Assets]docs/assets.md: embedded assets, public files, and `collect_static`.
- [Uploads]docs/uploads.md: multipart uploads, MIME screening, and file
  storage.
- [Signals]docs/signals.md: typed in-process event handling.
- [Channels]docs/channels.md: live client-facing pub/sub over SSE,
  WebSocket, and long polling.
- [Emitters]docs/emitters.md: cron, periodic, debounced, and notification
  sources.
- [Logging]docs/logging.md: tracing setup, sinks, and runtime logging.
- [Console]docs/console.md: optional JSON APIs for operations, task records,
  and runtime status.
- [Framework comparison]docs/comparison.md: `vyuh` vs Rocket, Utoipa,
  Actix, and FastAPI.

See [docs/index.md](docs/index.md) for the full documentation index.

## Backend Support

Vyuh is Postgres-first where database semantics matter most, but the common
database and task surfaces support Postgres, MySQL, and SQLite.

Vyuh has no default backend feature. With no backend feature enabled, it uses
SQLite-compatible SQLx aliases, a shared in-memory SQLite default database URL,
and `MemoryTaskStore` for tasks. This is useful for quick starts, docs, local
experiments, and tests that do not need durable task storage.

Production applications should enable exactly one backend feature:

```toml
vyuh = { version = "0.2", features = ["postgres"] }
vyuh = { version = "0.2", features = ["mysql"] }
vyuh = { version = "0.2", features = ["sqlite"] }
```

Postgres is the preferred production backend for high-concurrency task workers
and notification emitters. MySQL is supported for SQLx access and task storage.
SQLite is useful for local, embedded, and single-process deployments.

## Current Caveats

- Vyuh is usable, but not API-stable yet. Expect breaking changes before a
  stable release.
- Services are in-process and not durable.
- Tasks provide durable single-task continuations, not multi-task workflow
  orchestration.
- `MemoryTaskStore` is the no-backend default and is not durable.
- SQLite task storage is not positioned as a high-concurrency production worker
  backend.
- Some Postgres features, such as `LISTEN`/`NOTIFY` and `RETURNING *` helpers,
  are intentionally Postgres-only.

## License

Vyuh is licensed under the [MIT License](LICENSE).