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
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
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)
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
const copy = await apulodi.files.copy("file_8f2b…", {
path: "backups",
filename: "profile-backup.jpg",
});
Server-side copy; returns a new file id.
Download URL
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)
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
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:
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.
// 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.
Media transformation (video & audio)
The same transform API handles video and audio via the bundled ffmpeg pipeline:
// Transcode a video to mp4, scaled to 720p.
const v = await apulodi.files.transform("video_8f2b…", { format: "mp4", height: 720 });
// Extract a JPEG poster frame for <video poster>.
const poster = await apulodi.files.transform("video_8f2b…", { poster: true });
// Convert an audio file to mp3.
const a = await apulodi.files.transform("audio_8f2b…", { format: "mp3" });
const done = await apulodi.files.waitForVariant("video_8f2b…", v.id);
Supported inputs: video mp4/webm/mov/mkv, audio mp3/m4a/wav/ogg/aac/flac. Source files up to 100 MB. Variants inherit the file's visibility — public files serve derivatives from the CDN. Full reference: Media transformation.
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.