BetterBase provides file storage with S3-compatible API and built-in policy engine.
- S3-Compatible - Works with AWS S3, Cloudflare R2, Backblaze B2, MinIO
- Policy Engine - Fine-grained access control
- Image Transformations - On-the-fly resizing, cropping, format conversion
- Signed URLs - Secure access to private files
- Bucket Management - Multiple buckets per project
# Initialize storage
bb storage init// betterbase.config.ts
export default defineConfig({
storage: {
provider: 's3', // s3, r2, backblaze, minio, managed
bucket: 'my-app-uploads',
region: 'us-west-2',
policies: [
{
bucket: 'avatars',
operation: 'upload',
expression: 'auth.uid() != null'
},
{
bucket: 'avatars',
operation: 'download',
expression: 'true'
}
]
}
})import { createClient } from '@betterbase/client'
const client = createClient({ url: 'http://localhost:3000' })
// Upload file
const { data, error } = await client.storage.upload('avatars', 'user.png', fileBlob)
// Upload with custom path
const { data, error } = await client.storage.upload('documents', 'folder/file.pdf', file)// Download file
const { data, error } = await client.storage.download('avatars/user.png')
// Get blob
const blob = data// Get public URL for a file
const { data: { url } } = client.storage.getPublicUrl('avatars', 'user.png')// Delete file
await client.storage.remove('avatars/user.png')// List files in bucket
const { data, error } = await client.storage.list('avatars')import { storage } from '@betterbase/core/storage'
// Upload
await storage.upload('avatars', 'user.png', fileBuffer)
// Download
const file = await storage.download('avatars', 'user.png')
// Generate signed URL
const signedUrl = await storage.signUrl('avatars', 'user.png', {
expiresIn: 3600 // seconds
})
// Delete
await storage.remove('avatars', 'user.png')Transform images on-the-fly:
// Resize
const url = client.storage.getPublicUrl('images', 'photo.jpg', {
transform: {
width: 800,
height: 600,
fit: 'cover'
}
})
// Crop
const url = client.storage.getPublicUrl('images', 'photo.jpg', {
transform: {
width: 200,
height: 200,
fit: 'crop',
position: 'center'
}
})
// Format conversion
const url = client.storage.getPublicUrl('images', 'photo.jpg', {
transform: {
format: 'webp',
quality: 80
}
})Transform options:
width,height- Target dimensionsfit-cover,contain,fill,inside,outsideposition-top,bottom,left,right,centerformat-webp,jpg,png,avifquality- 1-100
Define access policies in configuration:
storage: {
policies: [
// Allow authenticated users to upload avatars
{
bucket: 'avatars',
operation: 'upload',
expression: 'auth.uid() != null'
},
// Allow public read access
{
bucket: 'avatars',
operation: 'download',
expression: 'true'
},
// Only owner can delete
{
bucket: 'documents',
operation: 'delete',
expression: 'auth.uid() == resource.userId'
}
]
}Policy expressions support:
auth.uid()- Current user IDauth.role()- User role (admin, user)resource.*- File metadata
# Storage provider
STORAGE_PROVIDER=s3
# S3 configuration
STORAGE_BUCKET=my-bucket
AWS_REGION=us-west-2
AWS_ACCESS_KEY_ID=your-key
AWS_SECRET_ACCESS_KEY=your-secret
# Or use R2 (Cloudflare)
STORAGE_PROVIDER=r2# Initialize storage
bb storage init
# List buckets
bb storage list
# Upload file
bb storage upload ./image.jpg -b avatars
# Download file
bb storage download avatars/image.jpg -o ./downloaded.jpg- Use policies - Always define access policies
- Validate file types - Restrict allowed MIME types
- Set size limits - Configure max file size
- Use CDN - Consider CDN for public assets
- Compress images - Use transformations for optimization
- Configuration - Storage config
- Client SDK - Storage API
- Functions - Process uploaded files