cloudflare-r2
Cloudflare R2 S3-compatible object storage. Use for buckets, uploads, CORS, presigned URLs, or encountering R2_ERROR, CORS failures, multipart issues.
git clone --depth 1 https://github.com/secondsky/claude-skills /tmp/cloudflare-r2 && cp -r /tmp/cloudflare-r2/plugins/cloudflare-r2/skills/cloudflare-r2 ~/.claude/skills/cloudflare-r2SKILL.md
# Cloudflare R2 Object Storage
**Status**: Production Ready ✅ | **Last Verified**: 2025-12-27 | **v3.0.0**
**Contents**: [Quick Start](#quick-start-5-minutes) • [New Features](#new-r2-features-2025) • [Core R2 API](#core-r2-workers-api-quick-reference) • [Critical Rules](#critical-rules) • [Agents & Commands](#available-agents--commands) • [References](#when-to-load-references)
---
## Quick Start (5 Minutes)
### 1. Create R2 Bucket
```bash
bunx wrangler r2 bucket create my-bucket
```
**Bucket naming:** 3-63 chars, lowercase, numbers, hyphens only
### 2. Configure Binding
Add to `wrangler.jsonc`:
```jsonc
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"r2_buckets": [
{
"binding": "MY_BUCKET", // env.MY_BUCKET
"bucket_name": "my-bucket", // Actual bucket
"preview_bucket_name": "my-bucket-preview" // Optional: dev bucket
}
]
}
```
**CRITICAL:** `binding` = code access name, `bucket_name` = actual R2 bucket
### 3. Basic Upload/Download
```typescript
import { Hono } from 'hono';
type Bindings = {
MY_BUCKET: R2Bucket;
};
const app = new Hono<{ Bindings: Bindings }>();
// Upload
app.put('/upload/:filename', async (c) => {
const filename = c.req.param('filename');
const body = await c.req.arrayBuffer();
const object = await c.env.MY_BUCKET.put(filename, body, {
httpMetadata: {
contentType: c.req.header('content-type') || 'application/octet-stream',
},
});
return c.json({
success: true,
key: object.key,
size: object.size,
});
});
// Download
app.get('/download/:filename', async (c) => {
const object = await c.env.MY_BUCKET.get(c.req.param('filename'));
if (!object) {
return c.json({ error: 'Not found' }, 404);
}
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
'ETag': object.httpEtag,
},
});
});
export default app;
```
**Load `references/setup-guide.md` for complete setup walkthrough.**
---
## New R2 Features (2025)
**🆕 R2 SQL Integration** - Query CSV/Parquet/JSON data with distributed SQL. Analytics without ETL. **Load `references/r2-sql-integration.md`**
**🆕 Data Catalog (Apache Iceberg)** - Table versioning, time-travel queries, schema evolution. Spark/Snowflake integration. **Load `references/data-catalog-iceberg.md`**
**🆕 Event Notifications** - Trigger Workers on object changes (upload/delete). Automate image processing, backups, webhooks. **Load `references/event-notifications.md`**
**Advanced Features** - Storage classes, bucket locks (compliance), tus resumable uploads, SSE-C encryption. **Load `references/advanced-features.md`**
**Zero Trust Security** - Cloudflare Access integration with SSO, MFA, identity policies, audit logging. **Load `references/cloudflare-access-integration.md`**
**Performance Tuning** - Caching strategies, compression, range requests, ETags, monitoring best practices. **Load `references/performance-optimization.md`**
---
## Core R2 Workers API - Quick Reference
### put() - Upload Objects
```typescript
await env.MY_BUCKET.put(key, data, options?)
```
Upload with metadata, prevent overwrites with `onlyIf`. **Load `references/workers-api.md`** for complete R2PutOptions.
### get() - Download Objects
```typescript
const object = await env.MY_BUCKET.get(key, options?)
```
Returns `R2ObjectBody | null`. Supports range requests, conditional operations. **Load `references/workers-api.md`** for read methods (text(), json(), arrayBuffer(), blob()).
### head() - Get Metadata Only
```typescript
const object = await env.MY_BUCKET.head(key)
```
Check existence, get size, etag, metadata without downloading body. Useful for validation and caching.
### delete() - Delete Objects
```typescript
await env.MY_BUCKET.delete(key | keys[]) // Single or bulk (max 1000)
```
Bulk delete up to 1000 keys in single call. Always succeeds (idempotent).
### list() - List Objects
```typescript
const listed = await env.MY_BUCKET.list(options?)
```
Pagination with cursor, prefix filtering, delimiter for folders. **Load `references/workers-api.md`** for R2ListOptions.
### createMultipartUpload() - Large Files (>100MB)
```typescript
const multipart = await env.MY_BUCKET.createMultipartUpload(key, options?)
```
For files >100MB. **Load `references/common-patterns.md`** for complete multipart workflow with part upload and completion.
**Load `references/workers-api.md` when**: Need complete API reference, interface definitions (R2Object, R2ObjectBody, R2PutOptions, R2GetOptions), conditional operations, checksums, or advanced options.
---
## Critical Rules
### Always Do ✅
1. **Set contentType on uploads** - Files will download as binary otherwise
2. **Use batch delete** for multiple objects (up to 1000 keys)
3. **Set cache headers** for static assets (`cacheControl`)
4. **Use presigned URLs** for large client uploads
5. **Use multipart upload** for files >100MB
6. **Set CORS policy** before browser uploads
7. **Set expiry times** on presigned URLs (1-24 hours)
8. **Handle errors** with try/catch
9. **Use head()** when you only need metadata (not get())
10. **Use conditional operations** to prevent overwrites
### Never Do ❌
1. **Never expose R2 access keys** in client-side code
2. **Never skip contentType** (files will download as binary)
3. **Never delete in loops** (use batch delete)
4. **Never upload without error handling**
5. **Never skip CORS** for browser uploads
6. **Never use multipart for small files** (<5MB overhead)
7. **Never delete >1000 keys** in single call (will fail)
8. **Never assume uploads succeed** (always check response)
9. **Never skip presigned URL expiry** (security risk)
10. **Never hardcode bucket names** (use bindings)
---
## Top Use Cases
### Use Case 1: Image/Asset Storage
```typescript
app.put('/api/upload/image', async (c) => {
const file = await c.req.parseBody();
const image = filRole-based access control (RBAC) with permissions and policies. Use for admin dashboards, enterprise access, multi-tenant apps, fine-grained authorization, or encountering permission hierarchies, role inheritance, policy conflicts.
100+ animated React components (Aceternity UI) for Next.js with Tailwind. Use for hero sections, parallax, 3D effects, or encountering animation, shadcn CLI integration errors.
shadcn/ui AI chat components for conversational interfaces. Use for streaming chat, tool/function displays, reasoning visualization, or encountering Next.js App Router setup, Tailwind v4 integration, AI SDK v5 migration errors.
Vercel AI SDK v5 for backend AI (text generation, structured output, tools, agents). Multi-provider. Use for server-side AI or encountering AI_APICallError, AI_NoObjectGeneratedError, streaming failures.
Vercel AI SDK v5 React hooks (useChat, useCompletion, useObject) for AI chat interfaces. Use for React/Next.js AI apps or encountering parse stream errors, no response, streaming issues.
Secure API authentication with JWT, OAuth 2.0, API keys. Use for authentication systems, third-party integrations, service-to-service communication, or encountering token management, security headers, auth flow errors.
Creates comprehensive API changelogs documenting breaking changes, deprecations, and migration strategies for API consumers. Use when managing API versions, communicating breaking changes, or creating upgrade guides.
Verifies API contracts between services using consumer-driven contracts, schema validation, and tools like Pact. Use when testing microservices communication, preventing breaking changes, or validating OpenAPI specifications.