Use R2 in a real application path
R2 is Cloudflare object storage for files, uploads, generated assets, and private objects.
This example uses one private bucket and one route, which is still the cleanest default shape for many real apps.
A good first R2 example teaches both the binding and the delivery boundary: the worker decides what the browser gets.
- Config focus
- Direct bucket naming
- Runtime shape
- Get an object from R2 and stream it through a route
- Best use
- Private file delivery or media endpoints
Start by wiring the binding clearly in config
Minimal R2 config
Build the application flow around the binding
Treat this as the app-level R2 path: the route, event handler, or service module receives a real request and uses the binding to do useful work.
Keep product limits, remote ownership, and fallback behavior visible in the code around the binding instead of hiding everything behind a vague utility too early.
- This route pattern keeps auth, caching, and content-type decisions in your app instead of in an assumed bucket URL contract.
- If you later choose a public bucket, make that an explicit architecture decision rather than a hidden side effect.
Serve an object through the worker
Keep production boundaries visible
- Config focus: Direct bucket naming.
- Runtime shape: Get an object from R2 and stream it through a route.
- Best use: Private file delivery or media endpoints.
A better first instinct than “just use the bucket URL”
Routing through the worker teaches the real boundary between stored objects and browser-facing responses.
Previous
Testing R2
R2 is local-friendly, which means you can test real object operations without inventing a storage adapter just to get off the ground.
Next
Durable Objects
The fast Devflare payoff is simple: put one counter object in a file, call it from the worker, and call the same object directly in tests.