xiaohongshu
xiaohongshu
hono-r2-integration
Use this skill whenever the user wants to design, set up, or refactor Cloudflare R2 object storage usage in a Hono + TypeScript app running on Cloudflare Workers/Pages, including bucket bindings, upload/download flows, signed URLs, and folder-like organization.
Full skill instructions
You are a specialized assistant for using Cloudflare R2 (S3-compatible object storage) inside a Hono + TypeScript app running on Cloudflare Workers / Pages.
Use this skill to:
c.env.BUCKET)Do not use this skill for:
hono-app-scaffoldhono-d1-integrationIf CLAUDE.md exists, obey its rules for bucket naming, storage paths, and access patterns.
Trigger this skill when the user asks for things like:
Avoid this skill when:
Assume:
App runs on Cloudflare Workers or Pages Functions.
R2 is configured as a binding in wrangler.toml, e.g.:
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket"
preview_bucket_name = "my-bucket-dev"
In code, the bucket is available as c.env.BUCKET and has type R2Bucket.
We type Env and encapsulate R2 access in services where possible.
This skill expects or creates something like:
src/
app.ts
index.ts
routes/
v1/
upload.routes.ts
files.routes.ts
storage/
r2-client.ts # helpers & Env typings
file-storage.service.ts
types/
env.d.ts # Env interface with BUCKET binding
Adjust paths to match the existing project.
Define Env with an R2 bucket binding:
// src/types/env.d.ts
export interface Env {
BUCKET: R2Bucket;
// other bindings (DB, KV, etc)
}
Integrate with Hono context typing:
// src/types/app.ts
import type { Env } from "./env";
export type AppContext = {
Bindings: Env;
};
Use when creating the app:
import { Hono } from "hono";
import type { AppContext } from "./types/app";
export const app = new Hono<AppContext>();
This skill should ensure c.env.BUCKET is fully typed.
Create a small helper to access the bucket and wrap common operations:
// src/storage/r2-client.ts
import type { Env } from "../types/env";
export function getBucket(env: Env): R2Bucket {
return env.BUCKET;
}
export type R2ObjectInfo = {
key: string;
size: number;
uploaded: string;
etag?: string;
httpEtag?: string;
checksums?: Record<string, string>;
};
This module can be extended with helper functions such as putObject, getObject, deleteObject etc., but often we call bucket methods directly from a service class.
Create a service that receives Env and encapsulates R2 operations.
// src/storage/file-storage.service.ts
import type { Env } from "../types/env";
import { getBucket } from "./r2-client";
export class FileStorageService {
constructor(private env: Env) {}
private bucket() {
return getBucket(this.env);
}
buildKey(prefix: string, filename: string): string {
// Example: "users/<prefix>/<filename>"
return `${prefix}/${filename}`;
}
async uploadObject(
prefix: string,
filename: string,
body: ReadableStream | ArrayBuffer | Uint8Array,
contentType?: string,
) {
const key = this.buildKey(prefix, filename);
const bucket = this.bucket();
const putResult = await bucket.put(key, body, {
httpMetadata: contentType ? { contentType } : undefined,
});
return { key, etag: putResult?.etag };
}
async getObject(key: string) {
const bucket = this.bucket();
const obj = await bucket.get(key);
if (!obj) return null;
return {
key,
body: obj.body,
size: obj.size,
uploaded: obj.uploaded,
httpMetadata: obj.httpMetadata,
};
}
async deleteObject(key: string) {
const bucket = this.bucket();
await bucket.delete(key);
}
async list(prefix: string) {
const bucket = this.bucket();
const res = await bucket.list({ prefix });
return res.objects;
}
}
This skill should:
users/<userId>/avatars/<file> or tenants/<tenantId>/...obj.body) correctly for Workers.Supports multipart/form-data uploads using c.req.parseBody() or c.req.formData() depending on Hono version.
// src/routes/v1/upload.routes.ts
import { Hono } from "hono";
import type { AppContext } from "../../types/app";
import { FileStorageService } from "../../storage/file-storage.service";
export function uploadRoutes() {
const app = new Hono<AppContext>();
app.post("/files", async (c) => {
const formData = await c.req.formData();
const file = formData.get("file");
if (!(file instanceof File)) {
return c.json({ message: "file field is required" }, 400);
}
const userId = "anonymous"; // or from auth middleware, e.g. c.get("user").id
const storage = new FileStorageService(c.env);
const key = storage.buildKey(`users/${userId}`, file.name);
const arrayBuffer = await file.arrayBuffer();
await storage.uploadObject(`users/${userId}`, file.name, arrayBuffer, file.type);
return c.json({ key }, 201);
});
return app;
}
This skill should:
// src/routes/v1/files.routes.ts
import { Hono } from "hono";
import type { AppContext } from "../../types/app";
import { FileStorageService } from "../../storage/file-storage.service";
export function filesRoutes() {
const app = new Hono<AppContext>();
app.get("/files/:key", async (c) => {
const keyParam = c.req.param("key");
// You may want to decode / validate this; often we pass encoded keys or IDs instead
const key = decodeURIComponent(keyParam);
const storage = new FileStorageService(c.env);
const obj = await storage.getObject(key);
if (!obj) {
return c.json({ message: "Not found" }, 404);
}
const headers: Record<string, string> = {};
if (obj.httpMetadata?.contentType) {
headers["Content-Type"] = obj.httpMetadata.contentType;
}
return new Response(obj.body, {
status: 200,
headers,
});
});
return app;
}
Important: Exposing raw keys in URLs may not be ideal; many apps use an opaque ID that maps to a key. This skill can recommend a mapping layer if needed.
R2 doesn’t provide S3-style presigned URLs in the same way out of the box, but you can:
Example idea:
// pseudo-code
app.get("/download/:token", async (c) => {
const token = c.req.param("token");
const { key } = await verifyDownloadToken(token); // JWT or similar
const storage = new FileStorageService(c.env);
const obj = await storage.getObject(key);
// stream object if valid
});
This skill should:
This skill should help you design key schemes like:
users/<userId>/avatars/<filename>users/<userId>/documents/<id>.pdftenants/<tenantId>/exports/<timestamp>.jsonpublic/<whatever> vs private/<userId>/<file>It should:
When hono-authentication is present, this skill can:
c.get("user") to determine userId or roles.Example pattern:
const user = c.get("user");
if (!user) return c.json({ message: "Unauthorized" }, 401);
// build prefix from user.id
const prefix = `users/${user.id}`;
This skill may recommend:
wrangler dev and preview buckets.R2Bucket methods in unit tests:const mockBucket: Partial<R2Bucket> = {
put: jest.fn().mockResolvedValue({ etag: "etag" } as any),
get: jest.fn().mockResolvedValue(null),
list: jest.fn().mockResolvedValue({ objects: [] } as any),
delete: jest.fn().mockResolvedValue(undefined),
};
wrangler dev and hit endpoints that talk to R2 directly.This skill must consider:
try {
await bucket.put(key, body);
} catch (err) {
console.error("R2 upload failed:", err);
return c.json({ message: "Upload failed" }, 500);
}
For these tasks, rely on this skill to build a clean, typed, and secure R2 integration inside your Hono + Cloudflare Workers/Pages application.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience