Browser client
Every Sluurp server serves its client, already in each app's import map: import { Sluurp } from "sluurp". Nothing to install. It wraps the REST API and manages auth.
import { Sluurp } from "sluurp";
const sluurp = new Sluurp();
const todos = sluurp.collection("todos");
const { items } = await todos.list({ filter: "done = false", sort: "-created" });
const todo = await todos.create({ title: "Milk" });
await todos.update(todo.id, { done: true }, { reason: "bought" });new Sluurp() talks to the page’s own server. Pass an origin, new Sluurp("https://school.example.com"), for another, and { project } for a non-default project.
Collections
sluurp.collection(name) has:
list({ page, perPage, sort, filter, asOf }) | One page: { items, page, perPage, totalItems, totalPages } |
listAll(options) | All pages as one array (served from sync after the first call) |
getOne(id), getFirst(filter) | A single record |
create(data), update(id, data), delete(id) | Writes. Each accepts { reason }, stored with the change |
history(id), version(id, seq), restore(id, seq) | Past versions, if history is on |
changes(since) | All changes after a sequence number, in order |
Files
upload(id, field, file) | Upload a File or Blob to a field |
createWithFile(data, field, file) | Create the record and upload in one step |
fileUrl(id, field, { w, h, fit, format }) | A URL for an <img>, resized |
srcset(id, field, [400, 800, 1200]) | A srcset at those widths |
fileObjectUrl(id, field), fileText(id, field) | Fetch a private file with auth |
Signing in
On the auth collection, usually users:
const users = sluurp.collection("users");
await users.authWithPassword(email, password);
sluurp.authStore.isValid; // signed in?
sluurp.authStore.record; // the user
sluurp.logout();Also: signUp, requestPasswordReset, requestSigninLink (magic link), verifyTwoFactor, authRefresh, and oauthUrl("google") with captureOAuthToken(sluurp) on the return page. The session is stored in localStorage and shared across the site’s pages. sluurp.onAuthFailure fires when the server rejects the token.
Permissions
const may = await sluurp.permissions();
may.can("grades", "update"); // for some rows at least
may.certainly("grades", "delete"); // for every rowEvaluates the collections’ rules up front, so you can show only buttons that will work.
Errors
Failed calls throw a SluurpError with status, message (from the server), body, and the helpers isAuthError (401) and isForbidden (403).
Live
sluurp.socket({ subscribe: ["messages"] }) opens a WebSocket that streams every change to those collections, filtered by your rules. on(type, listener) subscribes; join(topic) / leave(topic) handle presence; emit(topic, event, data) broadcasts without storing. For lists that stay current automatically, use Sync.
Instant first render
kept renders immediately from the last response cached in this browser, then updates from the server’s response if it differs (stale-while-revalidate):
sluurp.kept("feeds", () => sluurp.social.feeds(), (r) => feeds.set(r.items));A reload then shows the complete screen in the first frame instead of filling in piece by piece. The cache is per user, per browser, and cleared on logout().
And the rest
sluurp.conversation(id) and sluurp.conversations for chat, sluurp.pages for Pages, sluurp.payments and sluurp.billing for Payments, sluurp.ai for AI, sluurp.social for feeds. sluurp.send(path, { method, body, query }) calls any endpoint with auth and app headers attached.