Refactor Query contract: uniform ergonomics for SQLite + PostgreSQL
- Add `publish_key()` default method to `Query` trait (returns `None`) - Add `mutation_exec!` macro for SQLite INSERT/UPDATE/DELETE ops; overrides `publish_key()` so the mutation carries its own invalidation key - Add `Store::exec()` which runs a mutation and auto-publishes its key — action handlers no longer need a separate `store.publish()` call - Add `PgQuery` trait + `pg_query_one!` / `pg_query_all!` macros (feature-gated on `pg_replication`) mirroring the SQLite Query API - Add `cx.run_pg(pool, query)` to `RenderContext` which executes a PgQuery and auto-subscribes its cache key — no more manual `cx.subscribe()` calls - Update todo example: replace plain mutation fns with `mutation_exec!` structs and `store.exec()` calls; remove `action_then_publish` helper - Update pg_replication example: add `ListMessages` via `pg_query_all!` and replace manual subscribe + raw query with `cx.run_pg()` https://claude.ai/code/session_01HXe8rAZPweU9j2piBo9MdG
This commit is contained in:
@@ -46,12 +46,26 @@ use livevue_rs::{
|
||||
spawn_fanout, RenderContext, Store,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queries
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Fetch all messages, newest first.
|
||||
pub struct ListMessages;
|
||||
|
||||
livevue_rs::pg_query_all!(ListMessages {
|
||||
sql: "SELECT id, body FROM messages ORDER BY id DESC",
|
||||
params: [],
|
||||
output: Message,
|
||||
cache: |_| pg_table_key("public", "messages"),
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Domain types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
struct Message {
|
||||
pub struct Message {
|
||||
id: i64,
|
||||
body: String,
|
||||
}
|
||||
@@ -101,14 +115,8 @@ struct RenderState {
|
||||
}
|
||||
|
||||
async fn render_page(cx: &mut RenderContext, rs: &RenderState) -> anyhow::Result<Markup> {
|
||||
// Subscribe to PG-sourced invalidation events for the messages table.
|
||||
// The fanout task will re-render this connection whenever the WAL emitter
|
||||
// fires a `CacheKey::Channel { key: "pg:public.messages" }` event.
|
||||
cx.subscribe(pg_table_key("public", "messages"));
|
||||
|
||||
let messages: Vec<Message> = sqlx::query_as("SELECT id, body FROM messages ORDER BY id DESC")
|
||||
.fetch_all(&rs.pg)
|
||||
.await?;
|
||||
// Fetch messages and auto-subscribe to the WAL invalidation key.
|
||||
let messages = cx.run_pg(&rs.pg, ListMessages).await?;
|
||||
|
||||
let new_msg = cx.signal("newMsg", livevue_rs::global());
|
||||
let _conn_id_signal = cx.signal("connId", livevue_rs::global());
|
||||
|
||||
Reference in New Issue
Block a user