npm.io
0.5.0 • Published 20h ago

@rytass/storages-adapter-r2

Licence
MIT
Version
0.5.0
Deps
5
Size
25 kB
Vulns
0
Weekly
0
Stars
6

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

  • Cloudflare R2 object storage integration
  • S3-compatible API interface
  • Zero egress fees for data retrieval
  • Global edge distribution
  • Buffer and Stream file operations
  • Pre-signed URL generation
  • File existence checking and deletion
  • Cost-effective storage solution
  • 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

Keywords