This feature is in beta. Core behavior is stable, but some APIs or configuration may change before general availability.
Storage
EdgeBase Storage provides file storage built on Cloudflare R2 with $0 egress fees. Upload and download files with per-bucket access rules, generate time-limited signed URLs for secure sharing without authentication, and handle large files with resumable multipart uploads. Attach custom metadata to any file and control access with auth-aware read, write, and delete rules.
- Access Rules: Storage Access Rules gate bucket reads, uploads, and deletes.
- Hooks: Storage Hooks validate uploads, restrict downloads, and run file lifecycle side effects.
- Triggers: Storage is not a trigger-based surface.
- Handlers: Inline storage behavior lives under
storage.buckets[name].handlers.hooksinedgebase.config.ts.
Features
Upload / Download
Simple file operations with per-bucket access rules
Signed URLs
Time-limited access links for secure sharing (no auth required)
Multipart Upload
Resume-capable uploads for large files (state tracked via KV)
Metadata
Attach custom key-value metadata to any file
Access Rules
Per-bucket read, write, delete rules with auth context
Storage Hooks
Validate uploads, restrict downloads, and run post-upload or delete side effects
Quick Example
Assume client is already initialized with your platform SDK.
- JavaScript
- Dart/Flutter
- Swift
- Kotlin
- Java
- C#
- C++
const result = await client.storage.bucket('photos').upload('photo-001.jpg', file, {
customMetadata: { uploadedBy: currentUser.id },
});
const url = client.storage.bucket('photos').getUrl('photo-001.jpg');
const blob = await client.storage.bucket('photos').download('photo-001.jpg');
final bucket = client.storage.bucket('photos');
await bucket.upload(
'photo-001.jpg',
fileBytes,
contentType: 'image/jpeg',
customMetadata: {'uploadedBy': currentUser.id},
);
final url = bucket.getUrl('photo-001.jpg');
final bytes = await bucket.download('photo-001.jpg');
let bucket = client.storage.bucket("photos")
try await bucket.upload(
"photo-001.jpg",
data: imageData,
contentType: "image/jpeg",
customMetadata: ["uploadedBy": currentUser["id"] ?? ""]
)
let url = bucket.getUrl("photo-001.jpg")
let data = try await bucket.download("photo-001.jpg")
val bucket = client.storage.bucket("photos")
bucket.upload(
"photo-001.jpg",
fileBytes,
"image/jpeg",
mapOf("uploadedBy" to currentUserId)
)
val url = bucket.getUrl("photo-001.jpg")
val bytes = bucket.download("photo-001.jpg")
StorageBucket bucket = client.storage().bucket("photos");
bucket.upload("photo-001.jpg", fileBytes, "image/jpeg");
String url = bucket.getUrl("photo-001.jpg");
byte[] bytes = bucket.download("photo-001.jpg");
var bucket = client.Storage.Bucket("photos");
await bucket.UploadAsync("photo-001.jpg", fileBytes, "image/jpeg", new() {
["uploadedBy"] = currentUserId,
});
var url = bucket.GetUrl("photo-001.jpg");
var bytes = await bucket.DownloadAsync("photo-001.jpg");
auto bucket = client.storage().bucket("photos");
bucket.upload("photo-001.jpg", data, "image/jpeg");
std::string url = bucket.getUrl("photo-001.jpg");
auto bytes = bucket.download("photo-001.jpg");
// Signed URL (no auth needed to access)
const signedUrl = await admin.storage.bucket('photos').createSignedUrl('photo-001.jpg', {
expiresIn: '1h',
});
Server-side storage operations such as signed URL generation and privileged file access are available across all Admin SDKs.
Access Rules
// edgebase.config.ts
storage: {
buckets: {
photos: {
access: {
read() { return true }, // Public read
write(auth) { return auth !== null }, // Auth required
delete(auth, file) { return auth !== null && auth.id === file.uploadedBy },
},
},
},
}
Next Steps
Basic file operations, SDK usage
Time-limited access for secure file sharing
Resumable uploads for large files
Custom metadata on files
Per-bucket read, write, and delete control
Validate uploads and run lifecycle side effects
Choose Client SDK vs Admin SDK for storage workflows