npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rytass/storages-adapter-r2

v0.4.12

Published

Cloudflare R2 storage adapter

Readme

Rytass Utils - Cloudflare R2 Storage Adapter

High-performance storage adapter for Cloudflare R2, providing S3-compatible object storage with global distribution and zero egress fees. Offers the same interface as other storage adapters in the Rytass ecosystem.

Features

  • [x] Cloudflare R2 object storage integration
  • [x] S3-compatible API interface
  • [x] Zero egress fees for data retrieval
  • [x] Global edge distribution
  • [x] Buffer and Stream file operations
  • [x] Pre-signed URL generation
  • [x] File existence checking and deletion
  • [x] Cost-effective storage solution
  • [x] High availability and durability

Installation

npm install @rytass/storages-adapter-r2
# or
yarn add @rytass/storages-adapter-r2

Configuration

StorageR2Options

| Property | Type | Required | Description | | ----------- | -------- | -------- | ------------------------- | | bucket | string | Yes | R2 bucket name | | accessKey | string | Yes | R2 Access Key ID | | secretKey | string | Yes | R2 Secret Access Key | | accountId | string | Yes | Cloudflare Account ID | | region | string | No | R2 region (default: auto) |

Usage

Basic Setup

import { StorageR2Service } from '@rytass/storages-adapter-r2';

const storage = new StorageR2Service({
  bucket: 'my-r2-bucket',
  accessKey: 'your-access-key',
  secretKey: 'your-secret-key',
  accountId: 'your-cloudflare-account-id',
});

File Operations

import { readFileSync } from 'fs';

// Upload file
const fileBuffer = readFileSync('document.pdf');
const result = await storage.write(fileBuffer, {
  filename: 'documents/important-doc.pdf',
  contentType: 'application/pdf',
});

// Download file as buffer
const downloadedFile = await storage.read('documents/important-doc.pdf', {
  format: 'buffer',
});

// Download file as stream (default)
const fileStream = await storage.read('documents/important-doc.pdf');

// Generate public URL
const publicUrl = await storage.url('documents/important-doc.pdf');

// Check if file exists
const exists = await storage.isExists('documents/important-doc.pdf');

// Remove file
await storage.remove('documents/important-doc.pdf');

Stream Processing

import { createReadStream } from 'fs';

// Upload large file via stream
const fileStream = createReadStream('large-video.mp4');
const uploadResult = await storage.write(fileStream, {
  filename: 'media/videos/large-video.mp4',
  contentType: 'video/mp4',
});

Integration with File Converter

import { ConverterManager } from '@rytass/file-converter';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { StorageR2Service } from '@rytass/storages-adapter-r2';

const storage = new StorageR2Service({
  bucket: 'my-images',
  accessKey: process.env.R2_ACCESS_KEY!,
  secretKey: process.env.R2_SECRET_KEY!,
  accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
});

const manager = new ConverterManager([
  new ImageResizer({
    maxWidth: 1200,
    maxHeight: 800,
    keepAspectRatio: true,
  }),
]);

// Process the image
const processedImage = await manager.convert<Buffer>(imageFile);

// Upload to R2
const result = await storage.write(processedImage, {
  filename: 'processed-images/thumbnail.jpg',
  contentType: 'image/jpeg',
});

Environment Variables

# .env
R2_ACCESS_KEY_ID=your_r2_access_key
R2_SECRET_ACCESS_KEY=your_r2_secret_key
CLOUDFLARE_ACCOUNT_ID=your_account_id
R2_BUCKET_NAME=your-bucket-name
const storage = new StorageR2Service({
  bucket: process.env.R2_BUCKET_NAME!,
  accessKey: process.env.R2_ACCESS_KEY_ID!,
  secretKey: process.env.R2_SECRET_ACCESS_KEY!,
  accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
});

Error Handling

import { StorageError, ErrorCode } from '@rytass/storages';

try {
  const result = await storage.write(fileBuffer, { filename: 'path/to/file.pdf' });
} catch (error) {
  if (error instanceof StorageError) {
    switch (error.code) {
      case ErrorCode.FILE_NOT_FOUND:
        console.error('File not found');
        break;
      case ErrorCode.WRITE_FILE_ERROR:
        console.error('Failed to write file');
        break;
      case ErrorCode.READ_FILE_ERROR:
        console.error('Failed to read file');
        break;
      default:
        console.error('Storage error:', error.message);
    }
  }
}

R2 vs S3 Comparison

| Feature | Cloudflare R2 | Amazon S3 | | ------------------- | ------------------ | ------------------- | | Egress Fees | $0 | Charged per GB | | Global Distribution | Built-in | CloudFront required | | API Compatibility | S3-compatible | Native | | Pricing Model | Simple | Complex tiers | | Edge Computing | Cloudflare Workers | Lambda@Edge |

Best Practices

Cost Optimization

  • Leverage zero egress fees for frequently accessed content
  • Use R2 for serving static assets globally
  • Consider R2 for backup storage with frequent retrievals

Performance

  • Utilize Cloudflare's global network for faster access
  • Implement caching strategies at the edge
  • Use appropriate Content-Type headers for better caching

Security

  • Use IAM tokens with minimal required permissions
  • Enable bucket-level security policies
  • Implement proper access logging

Cloudflare R2 Setup

  1. Create R2 Bucket:

    # Via Cloudflare Dashboard or API
    curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account-id}/r2/buckets" \
      -H "Authorization: Bearer {api-token}" \
      -H "Content-Type: application/json" \
      --data '{"name":"my-bucket"}'
  2. Generate API Tokens:

    • Go to Cloudflare Dashboard → R2 → Manage R2 API tokens
    • Create token with appropriate permissions
  3. Configure CORS (if needed):

    {
      "allowed_origins": ["https://yourdomain.com"],
      "allowed_methods": ["GET", "PUT", "POST", "DELETE"],
      "allowed_headers": ["*"],
      "expose_headers": ["ETag"],
      "max_age": 3600
    }

Migration from S3

// Minimal code changes required due to S3-compatible interface
const storageR2 = new StorageR2Service({
  // R2 configuration
});

const storageS3 = new StorageS3Service({
  // S3 configuration
});

// Same interface, different storage backend
const operations = [
  storage.write(file, { filename: key }),
  storage.read(key),
  storage.remove(key),
  storage.isExists(key),
  storage.url(key),
];

License

MIT