SDK — Files

apulodi.files maps 1:1 to the Files REST API. Every method returns the same fully-typed object you get from the API (see the API reference).

Get a file

ts
const file = await apulodi.files.get("file_8f2b…");

file.filename;      // string
file.contentType;   // string
file.status;        // "uploaded"
file.version;       // 1
file.metadata;      // { userId: "u_123" } | null
file.createdAt;     // ISO string

List files

ts
const page = await apulodi.files.list({
  path: "users/avatars",
  limit: 50,
  sortBy: "size",
  order: "desc",
  search: "avatar",
});

Filters mirror the REST query params: search, path, contentType, status, plus sortBy/order. The response carries pagination.hasMore and pagination.nextCursor.

Update (rename / move / metadata)

ts
const updated = await apulodi.files.update("file_8f2b…", {
  filename: "profile.jpg",
  path: "users/profiles",
  metadata: { verified: true },
});

Renames and moves are logical-only — no bytes are copied.

Copy

ts
const copy = await apulodi.files.copy("file_8f2b…", {
  path: "backups",
  filename: "profile-backup.jpg",
});

Server-side copy; returns a new file id.

Download URL

ts
const { url, expiresAt } = await apulodi.files.downloadUrl("file_8f2b…", {
  expiresInSeconds: 300,
});

// fetch the bytes directly from storage
const res = await fetch(url);

Replace content (versioning)

ts
const v2 = await apulodi.files.replace("file_8f2b…", {
  data: newBytes,        // Buffer / Uint8Array / Blob / …
  contentType: "image/jpeg",
});

v2.version; // 2

replace() runs the initiate → PUT → complete steps for you; the previous version stays intact server-side.

Delete

ts
await apulodi.files.delete("file_8f2b…"); // { id, deleted: true }

Deletion soft-deletes the file: it disappears from all listings and reads return 404, but the storage object is kept for a grace window (APULODI_DELETE_GRACE_DAYS, default 7 days) before a periodic purge sweep removes it.

Restore

restore() re-activates a soft-deleted file while it is still inside the delete grace window. Immediately after a delete it succeeds:

ts
await apulodi.files.delete("file_8f2b…");
const back = await apulodi.files.restore("file_8f2b…"); // status: "uploaded"

Once the purge sweep has removed the object (after the grace window), restore fails with a typed ApulodiError (409, OBJECT_NOT_FOUND):

Image variants

Image files support variants — derived copies (thumbnails, resized renditions, format conversions) produced asynchronously without ever touching the original. Identical requests are idempotent: the same parameters always map to the same variant.

ts
// Fire-and-forget: returns immediately with a pending variant.
const variant = await apulodi.files.transform("file_8f2b…", {
  width: 256,
  height: 256,
  fit: "cover", // or "contain" / "inside"
  format: "webp", // or "jpeg" / "png" / "avif"
  quality: 80,
});

// Poll until ready (or subscribe to the file.processed webhook event).
const done = await apulodi.files.waitForVariant("file_8f2b…", variant.id);

// List every variant of a file.
const all = await apulodi.files.variants("file_8f2b…");

// Download the derivative directly from storage.
const { url } = await apulodi.files.variantDownloadUrl("file_8f2b…", done.id);

Every eligible image upload automatically gets a 256px webp thumbnail — the file.uploaded event triggers it, and file.processed / file.processing_failed report the outcome.

A note on identifiers

The API exposes filename and contentType (not name / mimeType) — the SDK types match the REST contract exactly, avoiding surprises when switching between raw API calls and the SDK.

Next: SDK — Uploads.