Crow NestDocs

Build apps

Data with crow.db

crow.db gives every app its own database of JSON documents, used straight from the page. There's no schema to set up and no keys: create a collection by adding to it, and set who can read and write in crow.json.

Load the library

html
<script src="/__crow/sdk.js"></script>

It's served by the app's own address, so it always matches. It defines window.crow; every call returns a Promise.

Who is using the app

js
const me = await crow.me();   // { id, email, name } for a signed-in viewer, or null when signed out
if (!me) crow.signIn();       // sends them to sign in and back to this page

Add, read, change, remove

js
const tasks = crow.db.collection("tasks");

const doc = await tasks.add({ title: "Ship it", done: false });   // the saved document with id, createdBy, createdAt, updatedAt
const one = await tasks.get(doc.id);                             // the document, or null
await tasks.update(doc.id, { done: true });                      // merges the fields you pass
await tasks.remove(doc.id);

Documents are up to 64 KB each. Crow Nest sets four fields you can read but not change: id, createdBy, createdAt and updatedAt (epoch milliseconds).

Query

js
const page = await tasks.list({
  where: [["done", "=", false], ["priority", ">=", 2]],   // up to 4 filters: = != < <= > >=
  order: "-createdAt",                                     // one field; "-" for descending
  limit: 50,                                               // 1 to 200, default 50
});
// page = { docs: [...], cursor }. Pass { ...the same query, cursor: page.cursor } for the next page; cursor is null at the end.

Watch for changes

js
const stop = tasks.watch({ order: "-createdAt" }, (page, err) => {
  if (err) return console.error(err);
  render(page.docs);
});
// stop() ends it.

watch updates live when people change the collection, and falls back to checking every 5 seconds when live updates aren't available.

Who can read and write

Set rules per collection in crow.json. Collections you don't list default to read: "viewers" and write: "creator".

json
{
  "data": {
    "tasks":    { "read": "viewers", "write": "creator" },
    "settings": { "read": "viewers", "write": "owner" }
  }
}
readWho sees documents
publicAnyone who can open the app, signed out too
viewersSigned-in people who can open the app
creatorEach person sees only their own
ownerOnly the app's owner
writeWho adds and changes
viewersAnyone signed in adds and changes anything
creatorAnyone signed in adds; people change only their own
ownerOnly the app's owner
publicSigned-out visitors add, and change their own entry
public-addSigned-out visitors can only add

Finding "mine"

By default other readers see only an author's id: createdBy is { id }. Compare it with the viewer:

js
const me = await crow.me();
const mine = page.docs.filter((d) => d.createdBy.id === me.id);

To show more, set showCreator to name or email on the collection (not allowed with read: "public").

Entries from signed-out visitors

On a public app, a collection can take entries from people who aren't signed in: a booking page, a contact form, a game's high scores.

json
{
  "data": {
    "rsvps":  { "read": "owner", "write": "public", "fields": { "name": "string:80", "email": "email?", "slot": "string:40" }, "unique": ["slot"], "editFor": "7d" },
    "scores": { "read": "public", "write": "public-add", "fields": { "name": "string:30", "score": "integer" }, "dailyLimit": 2000 }
  }
}
  • fields is required: entries must match it exactly. Types are string, string:N, number, integer, boolean, email, date, datetime, with ? for optional.
  • unique stops two entries sharing the same values, like one booking per slot. A clash answers conflict.
  • With public, a visitor can change their own entry later from the same browser, for as long as editFor allows. collection.editLink(id) gives a link that lets them do it from another device.
  • The library runs the bot check itself. Signed-out visitors can add 5 entries a minute and 50 a day each, and dailyLimit caps the collection.
  • The owner can block a visitor's entries from the dashboard's Data tab.

Errors

Calls throw an Error with a code: sign_in_required (a change by a signed-out visitor sends them to sign in), forbidden, not_found, bad_request, too_large, limit_reached, rate_limited, conflict, edit_closed or payment_required. See Error codes.

Limits

60 writes a minute per person; 100 MB of data per app (1 GB for companies). See Limits.

Seeing what's saved

The dashboard's Data tab shows each collection with its count, rules and recent documents, and exports everything as JSON. Your AI can use tool_data for the same view.