Clients
@pramen/client
A typed client: call() is RPC over HTTP, subscribe() is a live query over an
auto-reconnecting WebSocket. It is generic over your server's handler map, so calls
are fully typed with no runtime dependency on the server — import the type only.
import { createClient } from "@pramen/client";
import type { app } from "../server/app"; // type-only, erased at build
const pramen = createClient<typeof app.handlers>({ url, token, tenant: "acme" });
const note = await pramen.call("createNote", { title: "hi", body: "..." }); // typed
const stop = pramen.subscribe("listNotes", undefined, { onData: (notes) => render(notes) });
@pramen/react
Hooks that re-render on every server push:
import { useLiveQuery, useMutation } from "@pramen/react";
function Notes({ pramen }) {
const { data, loading } = useLiveQuery(pramen, "listNotes");
const createNote = useMutation(pramen, "createNote");
if (loading) return <p>Loading…</p>;
return (
<>
<ul>{data.map((n) => <li key={n.id}>{n.title}</li>)}</ul>
<button onClick={() => createNote({ title: "New", body: "" })}>Add</button>
</>
);
}
Because the client is generic over typeof app.handlers, handler names, inputs, and
result shapes are all checked at compile time — a typo or a wrong input type is a
build error, not a runtime surprise.