Skip to content

add-r2-storage

Configure R2 object storage for file uploads and media handling.

Overview

The add-r2-storage command sets up Cloudflare R2 storage buckets with secure upload/download endpoints, presigned URLs, and automatic file management.

Usage

bash
/template add-r2-storage <bucket-name> [options]

Parameters

  • <bucket-name> - Name of the R2 bucket to create (e.g., user-uploads, media-files)

Options

  • --public - Make bucket publicly accessible (default: private)
  • --max-size - Maximum file size in MB (default: 10)
  • --allowed-types - Comma-separated list of allowed MIME types
  • --prefix - URL prefix for serving files (default: /files)

Examples

Private User Uploads

bash
/template add-r2-storage user-uploads --max-size 5

Public Media Storage

bash
/template add-r2-storage media --public --allowed-types "image/*,video/*"

Document Storage

bash
/template add-r2-storage documents --allowed-types "application/pdf,application/msword"

What It Creates

R2 Configuration

  • Bucket binding in wrangler.toml
  • Upload/download API endpoints
  • File validation middleware
  • Presigned URL generation

Generated Structure

src/
├── storage/
│   ├── [bucket-name]/
│   │   ├── upload.ts      # Upload handler
│   │   ├── download.ts    # Download handler
│   │   ├── delete.ts      # Delete handler
│   │   ├── list.ts        # List files handler
│   │   └── utils.ts       # Storage utilities
│   └── index.ts           # Storage exports

Example Upload Endpoint

typescript
// src/storage/user-uploads/upload.ts
app.post('/api/upload', upload.single('file'), async (c) => {
  const file = c.req.file;
  const key = `uploads/${Date.now()}-${file.name}`;
  
  await c.env.USER_UPLOADS.put(key, file.stream(), {
    httpMetadata: {
      contentType: file.type,
    },
  });
  
  return c.json({ key, url: `/files/${key}` });
});

Example Download with Presigned URL

typescript
// src/storage/user-uploads/download.ts
app.get('/api/files/:key/url', async (c) => {
  const { key } = c.req.param();
  const url = await generatePresignedUrl(c.env.USER_UPLOADS, key, {
    expiresIn: 3600, // 1 hour
  });
  
  return c.json({ url });
});

Best Practices

  1. File Validation: Always validate file types and sizes
  2. Key Structure: Use organized key prefixes (e.g., users/{userId}/)
  3. Access Control: Implement proper authorization
  4. Cleanup: Set lifecycle rules for temporary files
  5. CDN Integration: Use Cloudflare CDN for public files

Security Features

  • File type validation
  • Size limits enforcement
  • Virus scanning integration ready
  • Access control per file
  • Secure presigned URLs

Common Patterns

Profile Pictures

bash
/template add-r2-storage avatars --public --max-size 2 --allowed-types "image/*"

Document Management

bash
/template add-r2-storage documents --max-size 50
/template implement-queue document-processing
bash
/template add-r2-storage media --public
/template add-r2-storage thumbnails --public
/template implement-queue thumbnail-generation

Integration Points

  • Workers KV: For file metadata
  • D1 Database: For file records
  • Queues: For async processing
  • Images API: For transformations

Performance Optimization

  • Multipart uploads for large files
  • Parallel chunk uploads
  • CDN caching for public files
  • Compression support
  • Range requests for streaming

Cost Considerations

  • Storage: $0.015 per GB/month
  • Class A operations: $0.36 per million
  • Class B operations: $0.036 per million
  • Egress: Free through Cloudflare

Built with ❤️ for the AI Coding community, by Praney Behl