Storage

PaperDB storage lets you upload, manage, and serve files backed by Cloudflare R2 (primary) or AWS S3 (fallback). All uploads go to real object storage — files are accessible via public URLs or time-limited presigned URLs.

Storage requires R2_ACCOUNT_ID / R2_BUCKET (Cloudflare R2) or S3_BUCKET / S3_ACCESS_KEY_ID (AWS S3) to be configured on the server. Requests to unconfigured servers return 503.

Uploading Files

Upload a single file from a browser input or Node.js Blob.

const file = document.getElementById('file-input').files[0];

const result = await db.storage.upload(file, {
  folder: "avatars",
  isPublic: true,
  metadata: { userId: "user_123" }
});

console.log("File uploaded to:", result.url);

Upload Multiple Files

const results = await db.storage.uploadMany([file1, file2, file3], {
  folder: "gallery",
  isPublic: true
});

Upload From URL

Fetch a remote file and store it directly in PaperDB. The server validates the URL against a private-IP blocklist to prevent SSRF.

const file = await db.storage.uploadFromUrl(
  "https://example.com/sample-image.png",
  { folder: "imports" }
);

Listing Files

const { files, total, hasMore } = await db.storage.list({
  folder: "avatars",
  limit: 10,
  offset: 0,
  sortBy: "createdAt",   // "createdAt" | "name" | "size"
  sortOrder: "desc"      // "asc" | "desc"
});

Get File Metadata

// By ID
const file = await db.storage.get("file_123");

// By storage path
const fileByPath = await db.storage.getByPath("avatars/abc123.png");

Move and Copy Files

Move a file to a different folder or rename it. Copy creates a new file record at the destination path.

// Move to a different folder
const moved = await db.storage.move("file_123", {
  folder: "archive",
  name: "old-avatar.png"   // optional rename
});

// Copy to a new location
const copy = await db.storage.copy("file_123", {
  folder: "backups"
});

Delete Files

// Delete single file
await db.storage.delete("file_123");

// Delete multiple files
await db.storage.deleteMany(["file_123", "file_456"]);

Signed URLs

Generate a temporary presigned URL for private file access. The URL is signed by R2/S3 and expires after the specified duration.

const { url, expiresAt } = await db.storage.getSignedUrl("file_123", {
  expiresIn: 3600 // seconds — default 1 hour
});

Image URL with Transform Parameters

getImageUrl() appends query parameters to the CDN URL. Actual image transformation requires a CDN or image proxy (e.g., Cloudflare Images, imgproxy) configured at your R2_PUBLIC_URL or S3_PUBLIC_URL — PaperDB passes the parameters through but does not process images itself.

const url = db.storage.getImageUrl(file, {
  width: 400,
  height: 400,
  fit: "cover",
  format: "webp"
});
// → https://your-cdn.com/path/to/file?w=400&h=400&fit=cover&f=webp

Folders

Folders are virtual — they are derived from the folder field on each file. You can create, list, and delete them.

// Create a folder (idempotent — safe to call multiple times)
await db.storage.createFolder("invoices/2024");

// List folders under a parent path
const folders = await db.storage.listFolders("invoices");

// Delete a folder and all files within it
const { deleted } = await db.storage.deleteFolder("invoices/old");

deleteFolder() permanently deletes all files inside the folder from both the database and the storage backend. This cannot be undone.