Skip to content
romaninshPublic

About

Business logic and persistence abstraction framework for Rust (enterprise grade) with strong type safety

Topics

Resources

Contributing

Stars

27 stars

Watchers

1 watching

Forks

Latest commit

 

History

1,647 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vantage

Book Ask DeepWiki

Vantage is a data framework for Rust. You describe your business entities (Client, Order, Invoice) once, as Rust types, and use them against SQL databases, SurrealDB, MongoDB, REST and GraphQL APIs, AWS, Kubernetes, CSV files or memory through one API.

Vantage works with sets of records, not single rows. A table with a condition is a smaller set. Traversing a relation turns one set into another. Counting, filtering and deleting run in the database whenever the database can do it, so the records never pass through your process.

There are three layers, and you can stop at any of them:

  • Table: typed entities, conditions, relations, expressions and CRUD, checked at compile time.
  • Vista: the same table with the types erased. It carries its own schema, can be loaded from YAML, and is what generic code (a CLI, an admin UI, an HTTP API) works with.
  • Diorama: a cache and live view over a Vista, for user interfaces. It loads the rows on screen, keeps them current and applies edits optimistically.

Looking for the app? Meet Vantage UI

If you want a finished product rather than a library, start with Vantage UI: a native, local-first admin console that you configure by talking to an AI coding agent. Point it at your databases, APIs, cloud accounts or CLI tools and describe the tool you need. The agent writes the configuration (YAML for structure, Rhai scripts for logic) into a folder you own, and the app draws tables, forms, dashboards and dialogs on top of your data.

Vantage UI is built on the crates in this repository. It is a free download for macOS. If you'd rather build your own service, CLI or UI, the rest of this README is for you.

Example

An entity and its table, from the book's second chapter:

use vantage_sql::prelude::*;
use vantage_types::prelude::*;

#[entity(SqliteType)]
#[derive(Clone, Default)]
pub struct Product {
    pub name: String,
    pub price: i64,
}

impl Product {
    pub fn table(db: SqliteDB) -> Table<SqliteDB, Product> {
        let is_deleted = Column::<bool>::new("is_deleted");
        Table::new("product", db)
            .with_id_column("id")
            .with_column_of::<String>("name")
            .with_column_of::<i64>("price")
            .with_column_of::<bool>("is_deleted")
            .with_condition(is_deleted.eq(false))
    }
}

The table definition lives in your model crate. Business code uses it without knowing where the data is stored:

let table = Product::table(db);

let all = table.list().await?;             // IndexMap<String, Product>
let pie = table.get("4").await?;           // Option<Product>

let muffin = Product { name: "Muffin".into(), price: 175 };
table.insert(&"8".to_string(), &muffin).await?;
table.delete(&"8".to_string()).await?;

Relations work on sets too. A category table declares .with_many("products", "category_id", Product::table), and then:

let products = Category::table(db)
    .with_search("t")
    .get_ref_as::<Product>("products")?;

That is one query, with a subquery inside it:

SELECT ... FROM "product"
WHERE "category_id" IN (SELECT "id" FROM "category" WHERE "name" LIKE '%t%')
AND "is_deleted" = 0

Writes are safe to retry. Inserting an id that already exists returns the stored row, replacing a row with the same values changes nothing, and deleting a row that is already gone succeeds. A write through a narrowed table can't touch rows outside its conditions.

Vista

Every driver ships a factory that turns a typed table into a Vista:

let table = Product::table(db.clone());
let vista = SqliteVistaFactory::new(db).from_table(table)?;

let mut v = vista.clone();
v.add_search("tart")?;
v.add_order("price", SortDirection::Descending)?;
let rows = v.list_values().await?;

A Vista has no generic parameters. Values are CBOR (ciborium::Value), so integers, decimals, datetimes and record ids keep their type. The Vista knows its columns, id column, title columns and references, and it declares what its driver can do (can_order, can_search, can_fetch_page, can_subscribe and so on). Asking for something the driver can't do returns an Unsupported error instead of a silently unfiltered result.

A Vista doesn't have to come from Rust code. Each factory also loads one from YAML:

name: category
columns:
  id: { type: string, flags: [id] }
  name: { type: string, flags: [title] }
references:
  products:
    table: product
    kind: has_many
    foreign_key: category_id

When YAML isn't enough, a Rhai script fills the gap. Here a Vista is defined by a query:

name: expensive_products
columns:
  id: { type: string, flags: [id] }
  name: { type: string }
sqlite:
  rhai: |
    select().from("product").field("id").field("name").where(expr("price > 150"))

vantage-rhai runs every script in a YAML file, with the same data vocabulary on every database. VistaCatalog in vantage-vista-factory follows references that cross databases, for example from a Postgres table into a MongoDB collection.

Diorama

vantage-diorama puts a cache and live updates between a Vista and a user interface:

let products = lens.make_dio(products_vista).await?;

// Use it like any other Vista, but reads are served from the cache:
let mut facade = products.vista();
facade.add_condition_eq("category", "books".into())?;
let books = facade.list_values().await?;

// Or open a view that updates when the data changes:
let scenery = products
    .table_scenery()
    .sort("price", SortDir::Asc)
    .open()
    .await?;

A scenery loads only the rows in its viewport and refreshes without clearing what's on screen. It polls less when nobody is looking and applies edits optimistically, rolling them back when the backend refuses. Views over the same query share one fetch. Diorama can also enrich rows with details from another source (augmentation), and vantage-diorama-aggregate keeps counts, sums and group-bys current as the data changes.

Datasources

Crate Backend Version Writes Stays current by
vantage-sql PostgreSQL, MySQL/MariaDB, SQLite 0.8 yes Postgres: LISTEN/NOTIFY (opt-in); others: poll
vantage-surrealdb SurrealDB (over surreal-client) 1.0 yes LIVE SELECT
vantage-mongodb MongoDB 1.1 yes change streams on a replica set, else poll
vantage-memory in-process tables 1.2 yes in-process events
vantage-redb redb embedded key-value store 0.7 yes typed tables only (no Vista yet)
vantage-api-client REST and GraphQL APIs 1.0 no poll
vantage-csv CSV files 0.6 no poll
vantage-aws AWS APIs (Logs, ECS, IAM, S3, Lambda, DynamoDB) 0.7 no poll
vantage-kubernetes Kubernetes API 0.6 no poll
vantage-cmd local CLI tools (aws, kubectl, gh) 0.6 no poll
vantage-spacetimedb SpacetimeDB (not yet on crates.io) 0.6 owner only subscription

The Live Data chapter explains what each backend does to stay current and what setup it needs.

The SQL crate also has query builders for each dialect (SqliteSelect, PostgresSelect, MysqlSelect) with joins, CTEs, window functions and vendor-specific functions. The SurrealDB crate has graph traversal. REST and GraphQL requests go through vantage-api-pool's ResilientClient, which handles parallelism limits, rate limits, retries, a circuit breaker and auth refresh.

vantage-faker generates seeded, related test data into vantage-memory, and can keep changing it with Rhai "sims" or make a source slow and unreliable on purpose, for testing UIs.

The crates

Foundation, all at 1.0 or later:

  • vantage-core: VantageError with context, error kinds and request priority.
  • vantage-types: per-database type systems (vantage_type_system!), Record, and the #[entity] macro.
  • vantage-expressions: query templates with parameters, deferred values that resolve at execution time, and the Selectable query builder trait.
  • vantage-dataset: read, write and insert traits for values and entities, active records, and an in-memory implementation.
  • vantage-table: Table<T, E> with columns, conditions, relations, expressions, aggregates and lifecycle hooks.
  • vantage-vista: the type-erased Vista and the TableShell trait drivers implement.
  • vantage-rhai: one Rhai host for every YAML script slot.

Live data and tooling:

  • vantage-diorama (0.14) and vantage-diorama-aggregate: cached, live views for UIs.
  • vantage-vista-factory: VistaCatalog, for references across databases.
  • vantage-inventory: loads a folder of YAML files into typed values, with !include, JSON-schema validation and file watching.
  • vantage-api-adapters: axum routers that serve a Dio over HTTP, with watch streams, and mount actions as POST routes.
  • vantage-action and vantage-bundle: business operations defined once on a model, and the contract a compiled-in model crate implements.
  • vantage-cli-util: terminal tables, JSON/NDJSON/CBOR output and a generic Vista CLI.
  • vantage-log-writer: append-only JSONL tables.

Installation

[dependencies]
vantage-core = "1"
vantage-types = "1"
vantage-dataset = "1"
vantage-table = "1"
vantage-sql = { version = "0.8", features = ["sqlite"] }  # also "postgres", "mysql", "vista", "rhai"
tokio = { version = "1", features = ["full"] }

Add the other drivers you need (vantage-surrealdb, vantage-mongodb, vantage-api-client, ...) and turn on their vista feature to use them through a Vista. The crates use edition 2024.

Learning

  • The book starts with the SQLite query builder and builds up to typed tables, an axum server and Vista. It then splits into two paths: a cached facade over a custom API (S3), or a live API over SQL that moves from SQLite to Postgres with LISTEN/NOTIFY and then to SurrealDB. Reference chapters cover expressions, relations, Rhai, live data and adding a new datasource.
  • learn-1 to learn-11 are the runnable projects behind those chapters.
  • bakery_model3 is a shared sample model with constructors for several backends, and bakery_model4 defines the same model as YAML Vistas.
  • The example_* crates show the model in egui, GPUI, Slint, Tauri, ratatui and Cursive.

For AI agents

  • The book as markdown: llms.txt, or the whole book in llms-full.txt

  • This repo as an MCP server, via GitMCP:

    claude mcp add --transport http vantage-docs https://gitmcp.io/romaninsh/vantage
  • Questions about the code: DeepWiki

Status

Each crate has its own version and changelog. The crates at 1.0 or later follow semver. The pre-1.0 crates (vantage-sql, vantage-diorama and the tooling crates) may still change between minor versions. vantage-aws, vantage-kubernetes, vantage-cmd and vantage-spacetimedb are incubating: read-only or partial, and their APIs are still moving.

Author

Vantage is written by Romans Malinovskis. To get in touch:

About

Business logic and persistence abstraction framework for Rust (enterprise grade) with strong type safety

Topics

Resources

Contributing

Stars

27 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages