Warning
s3mini is a deprecated alias retained solely for backward compatibility.
It is scheduled for removal in a future release. Please migrate to the new S3mini class.
Create a new instance of the s3mini client:
import { S3mini } from 's3mini';
// add S3Config types if needed for Typescript
const s3client = new S3mini({
accessKeyId: 'YOUR_ACCESS_KEY_ID',
secretAccessKey: 'YOUR_SECRET_ACCESS_KEY',
endpoint: 'https://s3.amazonaws.com/...', // S3 endpoint (use your region or custom domain) including bucket name
region: 'us-east-1',// AWS region (use 'auto' for Cloudflare R2)
maxRequestSizeInBytes?: 8388608, // Optional, defaults to 8MB
requestAbortTimeout?: 30000, // Optional, timeout in milliseconds
logger?: console, // Optional, custom logger
});const bucketExists = await s3client.bucketExists();
console.log(`Bucket exists: ${bucketExists}`);const created = await s3client.createBucket();
console.log(`Bucket created: ${created}`);const delimiter = '/';
const prefix = 'folder/';
const maxKeys = 1000;
const objects = await s3client.listObjects(delimiter, prefix, maxKeys);
console.log(`Objects: ${JSON.stringify(objects)}`);const delimiter = '/';
const prefix = 'folder/';
const uploads = await s3client.listMultiPartUploads(delimiter, prefix);
console.log(`Multipart uploads: ${JSON.stringify(uploads)}`);const fileContent = 'Hello, World!';
const key = 'example.txt';
const response = await s3client.putObject(key, fileContent);
console.log(`File uploaded successfully: ${response.status === 200}`);const key = 'example.txt';
const response = await s3client.getObject(key);
if (response) {
const content = await response.text();
console.log(`File content: ${content}`);
} else {
console.log('File not found');
}const key = 'example.txt';
const { etag, data } = await s3client.getObjectWithETag(key);
if (data) {
console.log(`File content: ${data}`);
console.log(`ETag: ${etag}`);
}const key = 'example.txt';
const exists = await s3client.objectExists(key);
console.log(`File exists: ${exists}`);const key = 'example.txt';
const etag = await s3client.getEtag(key);
console.log(`File ETag: ${etag}`);const key = 'example.txt';
const contentLength = await s3client.getContentLength(key);
console.log(`File size: ${contentLength} bytes`);const key = 'example.txt';
const response = await s3client.getObjectRaw(key);
// Process the raw responseconst key = 'example.txt';
const deleted = await s3client.deleteObject(key);
console.log(`File deleted: ${deleted}`);Requires a versioned bucket and a provider that implements the versioning S3 APIs
(ListObjectVersions, GetObject with versionId, versioned Delete/Copy).
Provider notes:
| Provider | Notes |
|---|---|
| MinIO / AWS S3 / Ceph | Full support; enable with setBucketVersioning('Enabled') (MinIO E2E does this in setup) |
| Backblaze B2 | Object versions work; setBucketVersioning may 403 — enable “keep all versions” in the B2 bucket settings |
| Cloudflare R2 | Not supported. R2 returns x-amz-version-id on Put for client compatibility, but ListObjectVersions / GetObject?versionId / PutBucketVersioning return 501 NotImplemented |
| Garage | No S3 object versioning |
await s3client.setBucketVersioning('Enabled'); // or 'Suspended'
const status = await s3client.getBucketVersioning(); // 'Enabled' | 'Suspended' | 'Off'Upload response headers may include x-amz-version-id (on some providers this is cosmetic only — verify with get-by-versionId).
// opts are forwarded as query parameters
const body = await s3client.getObject('file.jpg', { versionId: 'abc123' });// Returns every version (+ delete markers) for the exact key.
// Each entry may include VersionId, IsLatest, IsDeleteMarker.
const versions = await s3client.listObjectVersions('file.jpg');
const latest = versions?.find(v => v.IsLatest && !v.IsDeleteMarker);
const older = versions?.filter(v => !v.IsLatest && !v.IsDeleteMarker);// Same listObjects API; pass { versions: true }
const all = await s3client.listObjects('/', 'photos/', undefined, { versions: true });
// also works with listObjectsPaged(..., { versions: true })await s3client.copyObject('file.jpg', 'file.jpg', { versionId: olderVersionId });await s3client.deleteObject({ key: 'file.jpg', versionId: 'abc123' });
// Bulk: mix plain keys and versioned targets
await s3client.deleteObjects([
'other.txt',
{ key: 'file.jpg', versionId: 'v1' },
{ key: 'file.jpg', versionId: 'v2' },
]);const key = 'large-file.txt';
const fileType = 'text/plain';
const uploadId = await s3client.getMultipartUploadId(key, fileType);
console.log(`Multipart upload initiated with ID: ${uploadId}`);const key = 'large-file.txt';
const partContent = Buffer.from('Part content...');
const uploadId = 'your-upload-id';
const partNumber = 1;
const partResult = await s3client.uploadPart(key, partContent, uploadId, partNumber);
console.log(`Part uploaded: ${JSON.stringify(partResult)}`);const key = 'large-file.txt';
const uploadId = 'your-upload-id';
const parts = [
{ partNumber: 1, ETag: 'etag1' },
{ partNumber: 2, ETag: 'etag2' },
];
const result = await s3client.completeMultipartUpload(key, uploadId, parts);
console.log(`Multipart upload completed: ${JSON.stringify(result)}`);const key = 'large-file.txt';
const uploadId = 'your-upload-id';
const result = await s3client.abortMultipartUpload(key, uploadId);
console.log(`Multipart upload aborted: ${JSON.stringify(result)}`);import { sanitizeETag } from 's3mini';
...
const rawETag = '\"abcdef1234567890\"';
const sanitizedETag = s3client.sanitizeETag(rawETag);
console.log(`Sanitized ETag: ${sanitizedETag}`); // Outputs: abcdef1234567890Some operations can be rate-limited. Use the runInBatches method to process items in batches within a specified time interval:
import { runInBatches } from 's3mini';
const OP_CAP = 50; // Max operations per second
const INTERVAL = 1_000; // Interval in milliseconds
const generator = function* (n) {
for (let i = 0; i < n; i++)
yield async () => {
await s3client.putObject(`${prefix}object${i}.txt`, 'hello world');
};
};
// you can feed runInBatches with any async generator or array of promises/async functions
await runInBatches(generator(5000), OP_CAP, INTERVAL);Generate time-limited URLs for direct client access without credentials.
// Default expiration: 1 hour (3600 seconds)
const downloadUrl = await s3client.getPresignedUrl('GET', 'example.txt');
console.log(`Download URL: ${downloadUrl}`);// Expires in 5 minutes
const uploadUrl = await s3client.getPresignedUrl('PUT', 'uploads/file.bin', 300);
console.log(`Upload URL: ${uploadUrl}`);const uploadUrl = await s3client.getPresignedUrl('PUT', 'user-upload.jpg', 600);
// No credentials needed — use plain fetch
const response = await fetch(uploadUrl, {
method: 'PUT',
body: fileBuffer,
headers: { 'Content-Type': 'image/jpeg' },
});
console.log(`Upload success: ${response.ok}`);const downloadUrl = await s3client.getPresignedUrl('GET', 'user-upload.jpg');
const response = await fetch(downloadUrl);
const data = await response.arrayBuffer();const url = await s3client.getPresignedUrl('GET', 'report.pdf', 3600, {
'response-content-disposition': 'attachment; filename="report.pdf"',
'response-content-type': 'application/pdf',
});Enforce that the client sends specific HTTP headers when using the pre-signed URL. The server will reject requests where signed headers don't match.
// Upload URL that requires Content-Type — client MUST send this exact header
const url = await s3client.getPresignedUrl('PUT', 'uploads/data.json', 300, {}, {
'Content-Type': 'application/json',
});
await fetch(url, {
method: 'PUT',
body: JSON.stringify({ ok: true }),
headers: { 'Content-Type': 'application/json' }, // must match signed value
});The library throws descriptive error messages for invalid parameters and failed operations. Always use try-catch blocks when working with asynchronous operations:
try {
const result = await s3client.getObject('non-existent-file.txt');
// Process result
} catch (error) {
console.error(`Error: ${error.message}`);
}Many methods accept optional parameters for customization:
// Get with conditional headers
const result = await s3client.getObject('example.txt', {
'if-match': 'etag-value',
'if-modified-since': new Date().toUTCString(),
});The library supports custom loggers for better integration with your application's logging system:
const customLogger = {
info: message => {
/* Custom info logging */
},
error: message => {
/* Custom error logging */
},
warn: message => {
/* Custom warning logging */
},
};
const s3client = new S3mini({
// Other parameters
logger?: customLogger,
});