@radonsdk/storage26 providersOne upload() across 26 stores.
Object storage, CDN/media, self-hosted, and local — S3, R2, GCS, Azure, Supabase, Cloudinary, and more. Signed URLs, resumable uploads, and multi-provider failover, with every wire protocol hand-rolled so there are zero dependencies.
import { RadonStorage } from "@radonsdk/storage";
const storage = new RadonStorage({
providers: { s3: { bucket: "uploads", region: "us-east-1" } },
defaultProvider: "s3",
});
const { url } = await storage.upload({
key: "avatars/ada.png",
path: "./ada.png",
});26 providers
The whole S3-compatible family plus GCS, Azure Blob, Supabase, Bunny, ImageKit, Cloudinary, and self-hosted MinIO/Ceph/SeaweedFS.
Upload anything
Bytes, a stream, text, or a file path — the same upload({ key }) call, with content-type inferred for you.
Signed URLs
Time-limited links for private objects, signed with SigV4 — getUrl(key, { signed: true, expiresIn }).
Resumable & multipart
Chunked, resumable uploads (8 MiB parts) across the S3 family and Azure — for large files that must survive a dropped connection.
Multi-provider failover
Declare a chain — try S3, fall back to R2 — and writes automatically retry the next provider on failure.
Native escape hatch
storage.native() hands you the provider's own client for anything the unified API doesn't cover.
3 free, 23 on Pro — same call for every one.
A small, typed surface.
storage.upload({ key, path }): Promise<UploadResult>Upload from a path, Buffer, stream, or string; returns key + url.
storage.getUrl(key, { signed, expiresIn }): Promise<string>A public or time-limited signed URL for an object.
storage.download(key): Promise<Buffer>Read an object back into memory.
storage.list(prefix): Promise<ListResult>List objects and common prefixes, with cursor pagination.
storage.uploadResumable(input): Promise<UploadResult>PROChunked, resumable upload for large files.
storage.delete(key) · exists(key) · copy(src, dst)The rest of the object lifecycle, uniform across providers.
// A private invoice, shareable for one hour:
const link = await storage.getUrl("invoices/2026-01.pdf", {
signed: true,
expiresIn: 3600,
});getUrl and list don't fail over
Reads through the failover chain (upload, download, delete) retry the next provider — but getUrl and list always use the primary. Also: Cloudflare R2 is private by default, so set publicUrl or use signed URLs.