File storage is a planned feature for Baseflare that will be delivered in Phase 7a of the implementation roadmap. When implemented,Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nickruigrok/baseflare/llms.txt
Use this file to discover all available pages before exploring further.
ctx.storage will be available in queries, mutations, and actions, providing a clean API over Cloudflare R2. R2 is used because it is natively integrated with Cloudflare Workers, has zero egress fees, and requires no extra credentials or service accounts — the bucket is provisioned automatically alongside your Worker on first deploy.
Client Upload Flow
The recommended pattern for browser file uploads is a two-step flow. The client never sends files through your Worker — instead, it gets a signed URL and uploads directly to R2. This keeps your Worker fast and avoids hitting CPU or memory limits on large uploads.Generate a signed upload URL
Call a mutation that invokes
ctx.storage.generateUploadUrl(). This returns a short-lived signed URL pointing directly at your R2 bucket. The signed URL is safe to hand to the client.Upload the file directly to R2
The client receives the signed URL and POSTs the file directly to R2. On success, R2 returns a
storageId — a stable identifier for the stored object that can be passed back to your backend.Server-Side Upload
For server-side file handling — downloading a remote file, processing a webhook payload, or importing data — actions can upload directly to R2 usingctx.storage.store(). This method accepts a Blob and returns the storageId for the stored object.
ctx.storage.store() is only available in actions, not mutations. Mutations have access to generateUploadUrl() and delete() but not store(), because large I/O operations belong in actions. The StorageWriter and StorageActionWriter interfaces in baseflare/server encode this distinction at the type level.
Planned ctx.storage API
The ctx.storage type interfaces are already defined in packages/baseflare/src/server/functions/types.ts. The runtime implementation backed by env.FILES (the R2 binding) is what remains to be built as part of Phase 7a.
The source defines three interfaces with a clear inheritance chain:
QueryCtxreceivesStorageReader— read-only access viagetUrl(id)MutationCtxreceivesStorageWriter— addsgenerateUploadUrl()anddelete(id)ActionCtxreceivesStorageActionWriter— addsstore(blob)for server-side uploads
| Method | Available In | Returns | Description |
|---|---|---|---|
ctx.storage.getUrl(id) | Queries, Mutations, Actions | Promise<string | null> | Returns a public or signed URL for a stored file, or null if the ID does not exist. |
ctx.storage.generateUploadUrl() | Mutations, Actions | Promise<string> | Generates a short-lived signed URL for direct client-to-R2 upload. |
ctx.storage.delete(id) | Mutations, Actions | Promise<void> | Permanently deletes the R2 object. |
ctx.storage.store(blob) | Actions only | Promise<string> | Uploads a Blob server-side and returns the storage ID. |
ctx.storage.getMetadata(id) is planned for Phase 7a to return file size, content type, and upload timestamp from R2 custom metadata, but is not yet defined in the type interfaces. It will be added to StorageReader when the R2 runtime adapter is implemented.R2 Binding
Each Baseflare environment has exactly one R2 bucket, provisioned automatically on first deploy and bound to the Worker asenv.FILES. The bucket is named bf-{project}-{env}-files following the same naming convention as all other Cloudflare resources managed by Baseflare.
The bucket is created via the Cloudflare API during npx baseflare deploy — you do not need to create it manually or add it to any configuration file. The CLI records the bucket name in .baseflare/project.json after provisioning.
R2 has zero egress fees, unlike S3 and most competing object storage services. This makes it especially well-suited for media-heavy applications, document storage, and any use case where files are read frequently by end users. Storage and operations are billed at standard R2 rates on your Cloudflare account, but you will never pay extra for bandwidth when files are delivered to clients.