linode-s3-utils
v1.0.1
Published
Clean, intuitive class-based S3-compatible object storage utilities with named parameters for Linode Object Storage and AWS S3
Downloads
11
Maintainers
Readme
linode-s3-utils v1.0
Intuitive class-based S3-compatible object storage utilities with named parameters for Linode Object Storage and AWS S3.
✨ Features
- Class-based approach - Create once, use everywhere
- Named parameters - Self-documenting object-based API
- Environment variable support - Auto-configuration from env vars
- Auto content-type detection - Infers MIME types from file extensions
- Flexible configuration - Works with Linode Object Storage and AWS S3
- Clean error handling - Consistent success/error response format
Installation
npm install linode-s3-utilsQuick Start
const S3Utils = require('linode-s3-utils')
// Create an S3 instance with your configuration
const s3 = new S3Utils({
region: 'us-east-1',
accessKeyId: 'your-access-key',
secretAccessKey: 'your-secret-key'
})
// Upload a file - self-documenting and clear!
const result = await s3.uploadFile({
filePath: './myfile.jpg',
key: 'uploads/myfile.jpg',
bucket: 'my-bucket',
contentType: 'image/jpeg',
acl: 'public-read'
})
console.log(result.publicUrl)
// Generate presigned URL for client uploads
const presignedUrl = await s3.getPresignedUploadUrl({
key: 'uploads/photo.jpg',
bucket: 'my-bucket',
contentType: 'image/jpeg',
expiresIn: 300
})
// Delete an object
await s3.deleteObject({
key: 'old-file.jpg',
bucket: 'my-bucket'
})Environment Variables
Set environment variables and create instance without config:
export LINODE_BUCKET_REGION=us-east-1
export LINODE_S3_ACCESS_KEY=your-access-key
export LINODE_S3_SECRET_KEY=your-secret-keyconst S3Utils = require('linode-s3-utils')
const s3 = new S3Utils() // Uses environment variables automatically
const result = await s3.uploadData({
data: 'Hello World!',
key: 'files/hello.txt',
bucket: 'my-bucket',
contentType: 'text/plain'
})API Reference
Constructor
const s3 = new S3Utils(config)Parameters:
config.region(string): AWS/Linode regionconfig.accessKeyId(string): Access key IDconfig.secretAccessKey(string): Secret access keyconfig.endpoint(string, optional): Custom endpoint (auto-generated for Linode)config.forcePathStyle(boolean, optional): Force path-style addressing (default: true)
Methods
s3.uploadFile(params)
Upload a file from the local filesystem.
const result = await s3.uploadFile({
filePath: './photo.jpg',
key: 'photos/photo.jpg',
bucket: 'my-bucket',
contentType: 'image/jpeg', // optional, auto-detected
acl: 'public-read', // optional, default: 'public-read'
metadata: { // optional
'uploaded-by': 'user123',
'category': 'profile-pics'
}
})
if (result.success) {
console.log('File uploaded:', result.publicUrl)
}Parameters:
params.filePath(string): Local path to the fileparams.key(string): Object key (path) in the bucketparams.bucket(string): Bucket nameparams.contentType(string, optional): MIME type (auto-detected if not provided)params.acl(string, optional): Access control list (default: 'public-read')params.metadata(object, optional): Additional metadata to store with the object
s3.uploadData(params)
Upload data (Buffer or string) directly.
const result = await s3.uploadData({
data: 'Hello World!',
key: 'files/hello.txt',
bucket: 'my-bucket',
contentType: 'text/plain',
acl: 'private', // optional
metadata: { // optional
'source': 'api-generated'
}
})Parameters:
params.data(Buffer|string): Data to uploadparams.key(string): Object key (path) in the bucketparams.bucket(string): Bucket nameparams.contentType(string): MIME typeparams.acl(string, optional): Access control list (default: 'public-read')params.metadata(object, optional): Additional metadata to store with the object
s3.getPresignedUploadUrl(params)
Generate a presigned URL for client-side uploads.
const uploadUrl = await s3.getPresignedUploadUrl({
key: 'uploads/user-photo.jpg',
bucket: 'my-bucket',
contentType: 'image/jpeg',
expiresIn: 300, // 5 minutes
acl: 'public-read'
})Parameters:
params.key(string): Object key (path) in the bucketparams.bucket(string): Bucket nameparams.contentType(string, optional): MIME type (auto-detected if not provided)params.expiresIn(number, optional): URL expiration time in seconds (default: 180)params.acl(string, optional): Access control list (default: 'public-read')
s3.deleteObject(params)
Delete an object.
const result = await s3.deleteObject({
key: 'old-file.jpg',
bucket: 'my-bucket'
})Parameters:
params.key(string): Object key to deleteparams.bucket(string): Bucket name
s3.getPublicUrl(key, bucket)
Get public URL for an object.
const url = s3.getPublicUrl('photos/photo.jpg', 'my-bucket')Usage Patterns
Pattern 1: Single Instance for Entire App
// utils/s3.js
const S3Utils = require('linode-s3-utils')
const s3 = new S3Utils({
region: process.env.LINODE_BUCKET_REGION,
accessKeyId: process.env.LINODE_S3_ACCESS_KEY,
secretAccessKey: process.env.LINODE_S3_SECRET_KEY,
})
module.exports = s3// anywhere in your app
const s3 = require('./utils/s3')
const result = await s3.uploadFile({
filePath: './file.jpg',
key: 'uploads/file.jpg',
bucket: 'my-bucket'
})Pattern 2: Application-Specific Manager
class AppS3Manager {
constructor() {
this.s3 = new S3Utils({ /* config */ })
this.bucketName = 'my-app-bucket'
}
async uploadUserAvatar(userId, imageBuffer, mimeType) {
return await this.s3.uploadData({
data: imageBuffer,
key: `users/${userId}/avatar.${mimeType.split('/')[1]}`,
bucket: this.bucketName,
contentType: mimeType,
metadata: { 'user-id': userId, 'type': 'avatar' }
})
}
async generateUserUploadUrl(userId, fileName, mimeType) {
return await this.s3.getPresignedUploadUrl({
key: `users/${userId}/uploads/${fileName}`,
bucket: this.bucketName,
contentType: mimeType,
expiresIn: 600
})
}
}
const appS3 = new AppS3Manager()
module.exports = appS3Pattern 3: Express Route Integration
const S3Utils = require('linode-s3-utils')
const s3 = new S3Utils({ /* config */ })
router.post('/upload', async (req, res) => {
const result = await s3.uploadData({
data: req.file.buffer,
key: `uploads/${req.file.originalname}`,
bucket: 'my-bucket',
contentType: req.file.mimetype,
metadata: {
'uploaded-by': req.user.id,
'upload-time': new Date().toISOString()
}
})
if (result.success) {
res.json({ url: result.publicUrl })
} else {
res.status(500).json({ error: result.error })
}
})Why Named Parameters?
Comparison: Positional vs Named Parameters
Traditional Approach (Confusing):
// Hard to remember order, unclear what each parameter does
await uploadFile('./file.jpg', 'uploads/file.jpg', 'my-bucket', 'image/jpeg', 'public-read')Our Approach (Clear):
// Self-documenting, clear, order-independent
await s3.uploadFile({
filePath: './file.jpg',
key: 'uploads/file.jpg',
bucket: 'my-bucket',
contentType: 'image/jpeg',
acl: 'public-read'
})Benefits:
- ✅ Self-documenting - parameter names make it clear what each value is
- ✅ Order independent - can specify parameters in any order
- ✅ Optional parameters - easy to omit optional parameters
- ✅ IDE support - better autocomplete and IntelliSense
- ✅ Less error-prone - harder to mix up parameter order
- ✅ Future-proof - easy to add new parameters without breaking changes
Error Handling
All methods return objects with a success boolean:
const result = await s3.uploadFile({
filePath: './file.jpg',
key: 'file.jpg',
bucket: 'bucket'
})
if (result.success) {
console.log('Success:', result.publicUrl)
} else {
console.error('Error:', result.error)
}Supported File Types
Auto-detection for common extensions:
- Images: jpg, jpeg, png, gif
- Documents: pdf, txt, json, html, css, js
- Media: mp3, mp4, mov, wav
License
MIT
