npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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-utils

Quick 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-key
const 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 region
  • config.accessKeyId (string): Access key ID
  • config.secretAccessKey (string): Secret access key
  • config.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 file
  • params.key (string): Object key (path) in the bucket
  • params.bucket (string): Bucket name
  • params.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 upload
  • params.key (string): Object key (path) in the bucket
  • params.bucket (string): Bucket name
  • params.contentType (string): MIME type
  • params.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 bucket
  • params.bucket (string): Bucket name
  • params.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 delete
  • params.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 = appS3

Pattern 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