High-level Cloudflare R2 storage API wrapper for generating pre-signed URLs and performing object operations
Bucket operations provide functionality for managing buckets and checking the existence of buckets and objects.
Core Capabilities:
Key Methods:
R2Client.bucket(name: string): R2Bucket - Factory method to create bucket instancesR2Client.listBuckets(): Promise<BucketInfo[]> - List all account bucketsR2Bucket.getName(): string - Get bucket name (synchronous)R2Bucket.exists(): Promise<boolean> - Check if bucket exists (non-throwing)R2Bucket.getInfo(): Promise<BucketDetails> - Get bucket details (throws if not exists)R2Bucket.objectExists(key: string): Promise<boolean> - Check if object exists (non-throwing)R2Client.getBaseUrl(): string - Internal: Get base URL for R2 APIR2Client.getAwsClient(): AwsClient - Internal: Get AWS signing clientDefault Behaviors:
listBuckets() returns empty array [] if account has no buckets (not an error)exists() returns false for non-existent buckets (does not throw)getInfo() throws error if bucket doesn't exist (use exists() first to check)objectExists() returns false for non-existent objects (does not throw)getName() is synchronous and always returns the bucket namelocation: "auto" (automatic location constraint)creationDate may be undefined for some buckets (depends on R2 API response)Threading Model:
exists() or objectExists() calls are safegetName() is synchronous and thread-safeLifecycle:
R2Client.bucket() are reusablelistBuckets())Common Patterns:
if (await bucket.exists()) { /* operate */ }if (await bucket.objectExists(key)) { /* download */ }const buckets = await r2.listBuckets(); const names = buckets.map(b => b.name);bucket.getName() for consistent logging across operationsIntegration Points:
GET /)getInfo()objectExists()https://{accountId}.r2.cloudflarestorage.comCritical Edge Cases:
getInfo() throws error, exists() returns falseobjectExists() returns false (not an error)listBuckets() returns empty array (not an error)creationDate (undefined)location: "auto" (not region-specific)exists() and operation, operation will failException Handling:
getInfo() throws error if bucket doesn't existlistBuckets() throws error on authentication failure or network errorexists() and objectExists() never throw (return false on errors)getInfo() and listBuckets() in try-catchexists() and objectExists() are safe to call without try-catchGet a bucket instance from the R2Client for performing operations on a specific bucket.
/**
* Get a bucket instance for performing operations on a specific bucket
* @param name - The name of the bucket
* @returns An R2Bucket instance for the specified bucket
*/
bucket(name: string): R2Bucket;Usage Examples:
import { R2Client } from '@cfkit/r2';
const r2 = new R2Client({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
});
// Create bucket instance
const bucket = r2.bucket('gallery');
// Multiple bucket instances
const galleryBucket = r2.bucket('gallery');
const uploadsBucket = r2.bucket('uploads');
const backupsBucket = r2.bucket('backups');List all buckets in the Cloudflare account.
/**
* List all buckets in the account
* Uses the S3-compatible ListBuckets API (GET /)
* @returns Array of bucket information with names, creation dates, and location
*/
listBuckets(): Promise<BucketInfo[]>;
interface BucketInfo {
/** Bucket name */
name: string;
/** Creation date */
creationDate?: Date;
/** Bucket location constraint (typically "auto" for R2) */
location: string;
}Usage Examples:
import { R2Client } from '@cfkit/r2';
const r2 = new R2Client({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
});
// List all buckets
const buckets = await r2.listBuckets();
console.log('Available buckets:', buckets.map(b => b.name));
// => ['gallery', 'uploads', 'backups']
// Access bucket details
buckets.forEach(bucket => {
console.log(`Bucket: ${bucket.name}`);
console.log(` Created: ${bucket.creationDate}`);
console.log(` Location: ${bucket.location}`);
});
// Check if a specific bucket exists in the list
const bucketNames = buckets.map(b => b.name);
const hasGallery = bucketNames.includes('gallery');
console.log('Gallery bucket exists:', hasGallery);Check if a bucket exists.
/**
* Check if the bucket exists
* @returns true if bucket exists, false otherwise
*/
exists(): Promise<boolean>;Usage Examples:
import { R2Client } from '@cfkit/r2';
const r2 = new R2Client({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
});
const bucket = r2.bucket('gallery');
// Check if bucket exists
const exists = await bucket.exists();
if (exists) {
console.log('Bucket exists');
} else {
console.log('Bucket does not exist');
}
// Conditional operations based on existence
if (await bucket.exists()) {
await bucket.uploadFile('photo.jpg', file, {
contentType: 'image/jpeg'
});
}Get detailed information about a bucket including its location.
/**
* Get bucket information and metadata
* Uses the S3-compatible GetBucketLocation API
* @returns Bucket details including name and location
*/
getInfo(): Promise<BucketDetails>;
interface BucketDetails {
/** Bucket name */
name: string;
/** Creation date */
creationDate?: Date;
/** Bucket location constraint (typically "auto" for R2) */
location: string;
}Usage Examples:
import { R2Client } from '@cfkit/r2';
const r2 = new R2Client({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
});
const bucket = r2.bucket('gallery');
// Get bucket information
const info = await bucket.getInfo();
console.log(`Bucket: ${info.name}`);
console.log(`Location: ${info.location}`);
// => Bucket: gallery
// => Location: auto
// All R2 buckets use the "auto" location
if (info.location === 'auto') {
console.log('This is an R2 bucket with automatic location');
}Get the name of the bucket instance.
/**
* Get the bucket name
* @returns The bucket name
*/
getName(): string;Usage Examples:
import { R2Client } from '@cfkit/r2';
const r2 = new R2Client({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
});
const bucket = r2.bucket('gallery');
// Get bucket name
const name = bucket.getName();
console.log('Bucket name:', name);
// => Bucket name: gallery
// Useful for logging or validation
function logOperation(bucket: R2Bucket, operation: string) {
console.log(`Performing ${operation} on bucket: ${bucket.getName()}`);
}
logOperation(bucket, 'upload');Check if an object exists in the bucket.
/**
* Check if an object exists in the bucket
* @param key - Object key (filename)
* @returns true if object exists, false otherwise
*/
objectExists(key: string): Promise<boolean>;Usage Examples:
import { R2Client } from '@cfkit/r2';
const r2 = new R2Client({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
});
const bucket = r2.bucket('gallery');
// Check if object exists
const exists = await bucket.objectExists('photo.jpg');
if (exists) {
console.log('Object exists');
} else {
console.log('Object does not exist');
}
// Conditional download based on existence
if (await bucket.objectExists('photo.jpg')) {
const downloadUrl = await bucket.presignedDownloadUrl('photo.jpg');
console.log('Download URL:', downloadUrl.url);
}
// Avoid overwriting existing files
const key = 'important-file.jpg';
if (await bucket.objectExists(key)) {
console.log('File already exists, skipping upload');
} else {
await bucket.uploadFile(key, file, {
contentType: 'image/jpeg'
});
}
// Check multiple objects
const keys = ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'];
const existenceChecks = await Promise.all(
keys.map(key => bucket.objectExists(key))
);
const existingFiles = keys.filter((_, index) => existenceChecks[index]);
console.log('Existing files:', existingFiles);The R2Client exposes some internal methods for advanced use cases. These are typically not needed for normal operations.
/**
* Get the base URL for R2 operations (internal use)
* @returns The base URL used for all R2 API requests
*/
getBaseUrl(): string;
/**
* Get the AWS client instance (internal use)
* @returns The underlying AwsClient instance from aws4fetch
*/
getAwsClient(): AwsClient;AwsClient type from aws4fetch:
// Reference to external type from aws4fetch package
interface AwsClient {
// Internal AWS signing client - see aws4fetch documentation
// Used for AWS Signature V4 request signing
}Usage Examples:
import { R2Client } from '@cfkit/r2';
const r2 = new R2Client({
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
});
// Get base URL (for debugging or custom requests)
const baseUrl = r2.getBaseUrl();
console.log('R2 Base URL:', baseUrl);
// => https://your-account-id.r2.cloudflarestorage.com
// Get AWS client (for advanced custom signing)
const awsClient = r2.getAwsClient();
// Use awsClient for custom S3-compatible API requestsWhen to use these methods:
R2Bucket instances should always be created using R2Client.bucket(). The constructor is documented here for completeness but should not be called directly.
/**
* R2Bucket constructor (internal - use R2Client.bucket() instead)
* @param name - Bucket name
* @param awsClient - AWS client instance for signing
* @param baseUrl - Base URL for R2 operations
*/
constructor(name: string, awsClient: AwsClient, baseUrl: string);Correct usage:
// ✓ Correct - use R2Client.bucket()
const bucket = r2.bucket('gallery');
// ✗ Incorrect - don't instantiate directly
// const bucket = new R2Bucket('gallery', awsClient, baseUrl);Bucket operations may throw errors when:
Always wrap operations in try-catch blocks:
try {
const buckets = await r2.listBuckets();
console.log('Buckets:', buckets);
} catch (error) {
if (error instanceof Error) {
console.error('Failed to list buckets:', error.message);
}
}Error Handling Patterns:
// Safe existence check (never throws)
const exists = await bucket.exists();
if (!exists) {
console.log('Bucket does not exist');
return;
}
// getInfo() throws if bucket doesn't exist
try {
const info = await bucket.getInfo();
console.log('Bucket info:', info);
} catch (error) {
if (error instanceof Error) {
console.error('Bucket not found or error:', error.message);
}
}
// objectExists() never throws (returns false)
const objectExists = await bucket.objectExists('photo.jpg');
if (!objectExists) {
console.log('Object does not exist');
}Install with Tessl CLI
npx tessl i tessl/npm-cfkit--r2