Product Docs Pricing Changelog
Start free Sign in
Docs / SDK reference / Storage

Storage

Server-only SDK calls — every method needs the service key. But uploads and downloads themselves go browser → R2 directly via presigned URLs. Your server hands out short-lived URLs; the browser uses them to talk to R2. Zero bytes through Flarelink or your own server.

Looking for a complete walkthrough (server route → browser upload → record key → display)? See File uploads, end to end.

Presigned upload

// SERVER — mint a URL, return it to the browser via your API const { url, signedHeaders } = await flarelink.storage .from("uploads") .createSignedUploadUrl("avatars/jane.png", { contentType: "image/png", expiresIn: 600, // optional; default 300s, clamped to [60, 3600] }) // BROWSER — PUT direct to R2 await fetch(url, { method: "PUT", headers: signedHeaders, // don't add extras — it'll break the signature body: file, // File | Blob | ArrayBuffer })

Send exactly signedHeaders on the PUT — adding or omitting a header (e.g. a different Content-Type) changes the SigV4 signature and R2 returns 403 SignatureDoesNotMatch.

Presigned download

const { url } = await flarelink.storage .from("uploads") .createSignedDownloadUrl("avatars/jane.png") // Use it anywhere a URL works: <img src={url} />, window.open(url), fetch(url)…

Server-only operations

// Delete one or more objects (sequential under the hood) await flarelink.storage.from("uploads").remove(["avatars/old.png"]) // List objects under a prefix (up to 1000 per call; page via cursor) const { objects, prefixes, nextCursor } = await flarelink.storage .from("uploads") .list({ prefix: "avatars/" }) // All buckets on this project's R2 account const buckets = await flarelink.storage.listBuckets()

Notes

  • expiresIn is clamped to [60, 3600] seconds. The default is 300s — fine for an upload that starts right after the URL is minted.
  • The service key grants access to any bucket on the project's R2 account — it's the trust boundary, same as a raw R2 keypair. "Attach bucket" in the dashboard is organisation, not a security boundary.
  • Browser uploads need the bucket's CORS to allow your app's origin. Flarelink keeps R2 CORS in sync with your auth Worker's trusted origins automatically — add an origin in the Auth panel and it propagates to attached buckets.
  • list returns at most 1000 objects per call; pass the returned nextCursor back in to page. prefixes are the pseudo-folders under your prefix.
  • Storage failures throw StorageError — see Error reference for R2_NOT_CONFIGURED / INVALID_SERVICE_KEY and friends.
Something unclear or missing? hello@flarelink.dev llms-full.txt ↗