# R2

> Store object bodies and metadata independently of application compute.

<div class="ce-block-head">
<p class="ce-block-lead">Store object bodies and metadata independently of application compute.</p>
<ul class="ce-facts">
<li><span>Library</span> <code>Runtime.Workers</code></li>
<li><span>npm</span> <code>@cloudflare/workers-types</code> 5.20260906.1</li>
</ul>
</div>

## Objects and access

Create a bucket and attach an `R2Bucket` binding to the Worker that needs it. R2 also exposes an S3-compatible API for other clients. Choose object keys and lifecycle rules around the files your application owns; the Worker binding is one way to access the bucket.

R2 provides [strong consistency for object operations](https://developers.cloudflare.com/r2/reference/consistency/). Cached HTTP responses have their own freshness rules. Preserve content metadata and stream large bodies instead of assuming every object fits in application memory.

## File Drop

On a `PUT`, this Worker saves the request body as a file in R2, with HTTP metadata such as `Content-Type`. On a `GET`, `writeHttpMetadata` copies that metadata into the response headers. Uploads count toward the free plan's 1 million Class A operations a month, and downloads toward its 10 million Class B operations. [Egress from R2](https://developers.cloudflare.com/r2/pricing/) is free.

```fsharp
open Fable.Core

module Workers = FSharp.CloudEdge.Runtime.Workers

type Env =
    abstract FILES: Workers.R2Bucket

[<ExportDefault>]
let worker: Workers.ExportedHandler<Env, obj, obj, obj> =
    Workers.ExportedHandler.Create(fetch = fun request env _ ->
        async {
            let key = Workers.Exports.URL(U2.Case1 request.url).pathname.Substring(1)
            match request.``method`` with
            | "PUT" ->
                let! bytes = request.arrayBuffer() |> Async.AwaitPromise
                let metadata = U2.Case1 request.headers
                let options = Workers.R2PutOptions.Create(httpMetadata = metadata)
                let! stored =
                    env.FILES.put(key, U5.Case2 bytes, options) |> Async.AwaitPromise
                return Workers.Exports.Response.json {| key = key; size = stored.size |}
            | _ ->
                let! found = env.FILES.get key |> Async.AwaitPromise
                match found with
                | Some file ->
                    let headers = Workers.Exports.Headers()
                    file.writeHttpMetadata headers
                    headers.set("etag", file.httpEtag)
                    let init = Workers.ResponseInit.Create(headers = headers)
                    return Workers.Exports.Response.Create(file.body, init)
                | None ->
                    let notFound = Workers.ResponseInit.Create(status = 404.)
                    return Workers.Exports.Response.Create("Not found", notFound)
        }
        |> Async.StartAsPromise
        |> U2.Case1)
```

<div class="ce-needs"><p><strong>Needs</strong> an R2 bucket binding named <code>FILES</code>.</p></div>


## Help verify the binding

Useful checks include streamed uploads and downloads, missing objects, HTTP metadata, conditional requests, and multipart completion. A compiling consumer does not establish hosted behavior. Record the package version, configuration, and observed result using the [verification guide](../../guide/verify-bindings.md).

## Related Pages

- [Data & Analytics](index.md)
- [Account setup](../control-plane/account-setup.md)
- [Worker bindings](../control-plane/worker-upload.md)

## NuGet packages

[Runtime.Workers 0.1.0](https://www.nuget.org/packages/FSharp.CloudEdge.Runtime.Workers/0.1.0).

See [installation and release availability](../../guide/packages.md).
