10Docs

Files & search

Two collection features for content-heavy apps: storage collections back files in R2 with the same path-scoped auth as data, and search declares full-text indexing on chosen fields.

Files, type: storage

A collection declared "type": "storage" is R2-backed: each document is a file (blob) addressed by its path, with metadata fields you declare. Auth is exactly the same rules model as any collection, so file access is scoped by path just like data.

storage collection, per-user files
{
  "users/$userId/files/$fileId": {
    "type": "storage",
    "fields": { "name": "String", "owner": "String!", "size": "UInt" },
    "rules": {
      "read":   "@user.id != null && $userId == @user.id",
      "create": "@user.id != null && $userId == @user.id && @newData.owner == @user.id",
      "update": "false",
      "delete": "@user.id != null && $userId == @user.id"
    }
  }
}
  • The path scopes the file. users/$userId/files/$fileId with $userId == @user.id means a user can only touch files under their own id. The path is the access boundary, enforced by the runtime rule.
  • Declared fields are the file’s metadata (name, size, content-type, whatever you need); the bytes are stored separately in R2 and streamed.
  • Mark owner (and any set-once metadata) ! so it can’t be reassigned.
  • Storage collections are offchain.

Upload and download go through the SDK: setFile(path, file, { metadata }) uploads a File (or null to delete); getFiles(path) lists files under a path. The same path-scoped rules apply.

SDK, setFile / getFiles
import { getCurrentUser, setFile, getFiles } from "@bounded-sh/client";

const user = getCurrentUser();
if (!user) throw new Error("Sign in first");

await setFile(`users/${user.id}/files/avatar`, file, {
  metadata: { name: file.name, owner: user.id },
});
const { data: files } = await getFiles(`users/${user.id}/files`);

Choosing between them and ordinary data

NeedUse
Store an uploaded image / pdf / blobtype: "storage" collection
Store structured metadata about a filethe storage collection's fields
Free-text find across titles / bodiessearch: { fields: [...] }
Exact-match / range / membership lookupsordinary filters (SDK / CLI queries)