Skip to content

Files

Upload, URL builders, protected-file tokens, multi-file fields, delete.

vb.files wraps the /api/v1/files/* endpoints. Pairs with the Files concept page for the server-side rules.

const file = inputEl.files[0]; // a Blob / File from <input type="file">
const meta = await vb.files.upload("posts", post.id, "cover", file);
// meta = {
// id: "u-…",
// filename: "uuid-…ext",
// originalName: "photo.jpg",
// size: 12345,
// mimeType: "image/jpeg",
// }

The record must exist already, and uploading needs its update_rule. The upload stores the file; setting the field to the returned filename is what attaches it to the record:

const post = await vb.collection("posts").create({ title: "Hi" });
const meta = await vb.files.upload("posts", post.id, "cover", file);
await vb.collection("posts").update(post.id, { cover: meta.filename });

The file must have been uploaded to that record and field: setting cover to a file uploaded elsewhere is refused with a validation error (admins excepted).

A file that no record names is deleted by the hourly cleanup once it’s a day old, and so is the old file after you set the field to a new one.

Multi-file fields accept an array; the response is meta[]:

const metas = await vb.files.upload("posts", post.id, "attachments", [file1, file2]);
await vb.collection("posts").update(post.id, { attachments: metas.map((m) => m.filename) });

FileMeta and UploadOptions are exported types (SDK 0.7.2). upload with one File returns one FileMeta, with an array an array.

The server enforces maxSize and mimeTypes from the field options before any bytes hit storage.

const url = vb.files.url(meta.filename);
// → "https://api.example.com/api/v1/files/uuid-…ext"
const thumb = vb.files.url(meta.filename, { thumb: "300x200" });
// → "https://api.example.com/api/v1/files/uuid-…ext?thumb=300x200"
const cropped = vb.files.url(meta.filename, { thumb: "400x300", fit: "cover" });
// → "…?thumb=400x300_cover"

The URL is a plain GET, suitable for <img src> / <a href>.

Fields with protected: true need a ?token= to fetch. A field with only rule-based protection is also served to a request with an Authorization header, but a plain <img src> sends none, so a token is the way to show it there. The SDK mints + caches one for you:

const tokenized = await vb.files.urlWithToken("posts", post.id, "private_doc", "uuid-…pdf");
// → "https://api.example.com/api/v1/files/uuid-…pdf?token=<jwt>"
// Or just the token: { token, expires_at, one_time }
const t = await vb.files.token("posts", post.id, "private_doc", "uuid-…pdf");

The token is good for ~1h (server-configurable via auth.file.window_seconds). The SDK caches it in memory per collection, record, field and filename, and refreshes it ~60s before expiry. Two exceptions:

  • One-time tokens aren’t cached. On a field with oneTimeToken, each token works for one download, so every call mints a fresh one. The SDK reads the server’s one_time flag (or the token’s uses: 1 claim, for older servers).
  • The cache is dropped when the signed-in user changes (sign-in, sign-out, another account), since a token was granted to the previous identity.

vb.files.clearTokenCache() drops every cached token yourself.

// One file from a multi-file field
await vb.files.delete("posts", post.id, "attachments", filename);
// Every file under (record, field): leave out the filename
await vb.files.delete("posts", post.id, "attachments");

vb.files.upload accepts any Blob / File-shaped object — useful in Node:

import { readFile } from "node:fs/promises";
const buf = await readFile("photo.jpg");
const blob = new File([buf], "photo.jpg", { type: "image/jpeg" });
await vb.files.upload("posts", post.id, "cover", blob);

Errors are VaultbaseErrors:

Statuse.dataCause
400kind: "server", status: 400No file in the request, or more than one for a single-file field
401kind: "auth", reason: "expired"Not signed in
403kind: "auth", reason: "forbidden"The record’s update_rule refuses the upload, a file rule refuses the download, or an IP-bound token is used from another address
404kind: "server", status: 404No such record or field
410kind: "server", status: 410One-time token replayed
413kind: "server", status: 413Over the server’s upload limit (files.max_upload_size)
415kind: "server", status: 415MIME type outside the server’s allow-list
422kind: "validation"MIME type outside the field’s mimeTypes, or over its maxSize; e.data.details names the field