@malikbusiness/f2-storage
v1.0.1
Published
Official Node.js and Browser SDK for MalikBusiness F2 Object Storage (powered by Cloudflare R2)
Downloads
359
Maintainers
Readme
@malikbusiness/f2-storage
Official Node.js and Browser SDK for MalikBusiness F2 Object Storage (powered by Cloudflare R2).
High-speed, zero-egress cloud storage for images, videos, PDFs, and files with S3 durability and global CDN delivery.
Installation
npm install @malikbusiness/f2-storageQuick Start
1. Initialize Client
import { F2Client } from '@malikbusiness/f2-storage';
// Or in CommonJS:
// const { F2Client } = require('@malikbusiness/f2-storage');
const f2 = new F2Client({
storageName: 'your-storage-name',
apiKey: 'f2_ak_your_api_key',
apiSecret: 'f2_sk_your_api_secret',
// Optional:
// timeout: 120000, // ms, default 2 minutes
// endpoint: 'https://api.malikbusiness.cloud/api/r2/v1', // default
});Keep
apiKey/apiSecreton the server (e.g. in.env). Anyone with them can write to your storage, so don't ship them in browser bundles.
2. Upload Files
Upload a local file (Node.js):
const result = await f2.uploader.upload('./uploads/avatar.png', {
folder: 'user-avatars',
filename: 'user_101.png',
});
console.log('CDN URL:', result.url);
console.log('Storage key:', result.key);
console.log('Size in bytes:', result.bytes);Upload from Express / Multer (req.file.buffer):
app.post('/api/upload', upload.single('image'), async (req, res) => {
try {
const result = await f2.uploader.upload(req.file.buffer, {
folder: 'products',
filename: req.file.originalname,
contentType: req.file.mimetype,
});
return res.json({ url: result.url });
} catch (err) {
return res.status(500).json({ error: err.message });
}
});Upload from Browser (File or Blob):
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const result = await f2.uploader.upload(file, {
folder: 'user-uploads',
});
console.log('Uploaded:', result.url);Upload Base64 Data URI:
const base64Data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
const result = await f2.uploader.upload(base64Data, {
folder: 'generated',
filename: 'pixel.png',
});Upload options
| Option | Description |
| ------------- | --------------------------------------------------------------------------- |
| bucket | Bucket name or display name. Defaults to your first bucket. |
| folder | Folder inside the bucket, e.g. products/2026. |
| filename | Destination filename. Unsafe characters are replaced with _. |
| contentType | MIME type. Guessed from the filename extension when omitted. |
Uploading to an existing folder/filename overwrites that file.
3. Response Structure
{
"success": true,
"url": "https://f2.malikbusiness.cloud/your-storage-name/user-avatars/user_101.png",
"secure_url": "https://f2.malikbusiness.cloud/your-storage-name/user-avatars/user_101.png",
"key": "user-avatars/user_101.png",
"filename": "user_101.png",
"format": "png",
"bytes": 204850,
"mimetype": "image/png",
"bucket": "default",
"storage_name": "your-storage-name",
"created_at": "2026-09-16T06:00:00.000Z"
}url is a permanent CDN link when the bucket is public. For a private bucket it is a signed link that expires after 24 hours, so make the bucket public if you store the URL (e.g. product images in a database).
4. Delete Files
Pass the key returned by upload:
await f2.uploader.destroy('user-avatars/user_101.png', { bucket: 'your-bucket' });5. Error Handling
Failed requests throw an Error with the API message, plus status (HTTP status) and code when the API provides one:
try {
await f2.uploader.upload(buffer, { filename: 'a.png' });
} catch (err) {
console.error(err.status, err.code, err.message);
// 401 INVALID_F2_CREDENTIALS Invalid or revoked F2 API credentials.
// 403 READ_ONLY_KEY Permission denied. This API key is read-only.
// 403 (quota) Account storage quota exceeded.
}Development
npm test # runs the SDK against a local mock API, no credentials neededReleasing
Releases are published to npm automatically by GitHub Actions (.github/workflows/publish.yml) on every push to main, when package.json has a version that isn't on npm yet:
npm version patch # 1.0.0 -> 1.0.1 (use minor / major for bigger changes)
git push --follow-tagsPublishing uses npm Trusted Publishing: npm trusts this repository's publish.yml workflow directly, so no npm token is stored in GitHub.
License
MIT © MalikBusiness
