cloudflare-r2
@noy-db/to-cloudflare-r2·pnpm add @noy-db/to-cloudflare-r2Zero egress fees, S3-API-compatible.
| Shape | record |
| Atomic CAS | ✓ |
| Locality | edge |
| Speed | slow |
| Persistence | durable |
What it does
A thin factory around @noy-db/to-aws-s3, pre-configured for the R2
endpoint (r2EndpointFor(accountId)). Same object-per-record shape and API surface
as S3 — just point it at your Cloudflare account.
Notes
prefixmust not end in/— forwarded totoAwsS3(), so it inherits that package's refusal (since0.7.0-pre.0); the error names@noy-db/to-aws-s3even when you calledtoCloudflareR2()(noy-db-to#109).casAtomic: true— inheritsto-aws-s3's conditional-write CAS (IfMatch/IfNoneMatch), plusserverWriteTime: true.- Record shape, one object per record, same
loadAll()O(N)-requests caveat as S3. - Factory:
toCloudflareR2(options: R2Options).
BREAKING: plaintext keys removed
accessKeyId / secretAccessKey are no longer accepted on R2Options.
Authentication now has exactly two paths — a credentials source or a
pre-built client — because the descriptor path cannot offer a plaintext
key pair without breaking the credentialless-by-construction rule, and
leaving it on the direct path would split auth into two diverging stories.
Migrate by wrapping the static keys in a credential source:
// before
toCloudflareR2({ bucket, accountId, accessKeyId, secretAccessKey })
// after
toCloudflareR2({ bucket, accountId, credentials: async () => ({
kind: 'aws', accessKeyId, secretAccessKey,
}) })
Store-locator descriptor
See store-locator descriptors for the general model.
kind: 'cloudflare-r2', class: 'cloud'. address is { bucket, accountId?, prefix?, endpoint? } — endpoint is location, not tuning, so it
lives here rather than in options, and supplements accountId rather than
replacing it. options is { clockUncertaintyMs? }. binding.client?
carries a pre-built S3Client (a shared R2-pointed client, a Workers
binding, or a test fake), which always wins over accountId + credentials.
R2 keys are S3-compatible, so credentials arrive via a kind: 'aws'StoreCredentialSource at resolve() time.
import { r2StoreDescriptor, registerR2Store } from '@noy-db/to-cloudflare-r2'
const descriptor = r2StoreDescriptor({ bucket: 'my-vault', accountId: '...' })
See also
- to-* family
- aws-s3 — the store this wraps
- cloudflare-d1