NukeBase

File Server API

Five file functions and two helpers are destructured from your app.js module argument. Unlike the synchronous data operations, the file operations are async — they touch the disk — so await them.

Server file operations
module.exports = ({ getFile, setFile, removeFile, listFiles,
                   fileStat, fileUrlFor, addFileTrigger, FILES_DIR, ... }) => {

  // Write. content may be a Buffer, TypedArray or string.
  const w = await setFile(["reports", "2026-08.csv"], csvString);
  // → { status: "Success", size: 4821, type: "text/csv", name: "2026-08.csv" }

  // Read. Resolves with a Buffer in .data.
  const r = await getFile(["reports", "2026-08.csv"]);
  // → { status: "Success", data: <Buffer>, name, size, type, mtime }
  // → { status: "Path not found", data: null }  if it isn't there

  // List one level.
  const l = await listFiles(["reports"]);
  // → { status: "Success", data: [{ name, isDir, size, type, mtime }, …] }
  // Capped at FILE_MAX_DIR_ENTRIES; a truncated listing sets .truncated = true

  // Delete. Directories require { recursive: true }.
  await removeFile(["reports", "2026-08.csv"]);
  await removeFile(["reports"], "root", { recursive: true });

  // Synchronous stat — no await, returns undefined if nothing is there.
  const st = fileStat(["reports", "2026-08.csv"]);
  // → { name, ext: ".csv", size, type, mtime, isDir: false }

  // Build the URL a client would fetch.
  fileUrlFor(["reports", "2026-08.csv"]);  // "/_file/reports/2026-08.csv"
};

Server-side file calls run as root by default. Every one of these takes an optional admin argument that defaults to the string "root", which bypasses file rules entirely — exactly like get/set on the data side. To have a call checked against your rules, pass the caller's auth context through explicitly:

addCallable("uploadAvatar", async (data, admin) => {
  // Checked against fileWrite/fileValidate as the *caller*, not as root
  return await setFile(["avatars", admin.uid + ".png"], data.bytes, admin);
});