Collections and rules
A collection is a real SQLite table. Its rules are compiled into the same query that fetches its rows, not checked afterwards.
Fields
The field types are text, number, bool, email, url, date, json, select, relation and file. A collection may declare composite and unique indexes. A collection that declares "extends": "_users" holds an app’s own profile of a person, under the same id as their identity.
Five rules
"rules": {
"list": "@request.auth.id != null",
"view": "@request.auth.id != null",
"create": "author = @request.auth.id",
"update": "author = @request.auth.id",
"delete": "author = @request.auth.id || @request.auth.staff = true"
}- With no rule, only superusers have access;
""means anybody. - A list rule filters: a caller gets only the rows they may see, and the counts stay correct.
- View, update and delete answer 404 rather than 403, so a rule can’t be used to learn that a row exists.
- After a write, the row is checked again inside its transaction. A write that would move a row out of the writer’s reach is rolled back.
Fields can have their own rules too, such as who may read a pupil’s grade or change a status. They are enforced in the same SQL.
The API
GET /api/collections/todos/records?filter=done=false&sort=-created
POST /api/collections/todos/records
PATCH /api/collections/todos/records/:id
DELETE /api/collections/todos/records/:id
// server-sent events, each row read as you
GET /api/realtime?subscribe=todosimport { Sluurp } from "sluurp";
const sluurp = new Sluurp();
await sluurp.collection("todos").create({ title: "Milk", author: sluurp.auth.id });
const open = await sluurp.collection("todos").list({ filter: "done = false" });Filters are parsed and compiled with bound parameters. A filter that names a field that doesn’t exist is refused before any SQL is built.
Projects
Each project is its own database file, and no query spans two. An app is pinned to one project, so its functions can’t reach another app’s data.