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

@aidalinfo/nuxt-file-storage-s3

v0.3.2

Published

Easy solution to store files in your nuxt apps. Be able to upload files from the frontend and recieve them from the backend to then save the files in your project.

Readme

Nuxt File Storage Banner

@aidalinfo/nuxt-file-storage-s3

Nuxt

Easy solution to store files in your Nuxt apps — supports local storage and S3-compatible storage (AWS S3, Garage, MinIO, DigitalOcean Spaces…).

Features

  • 📁  Handle file inputs on the frontend and serialize them for the backend
  • 🗜️  Optional client-side compression before serialization (images, PDF strategy-ready)
  • 🧾  Optional server-side PDF optimization before local/S3 storage (with safe fallback)
  • 🖴  Store files locally via Nitro server engine
  • ☁️  Store files on S3 or any S3-compatible service
  • 🔀  Unified API — same functions work for both local and S3, automatically detected from config

Quick Setup

npm install @aidalinfo/nuxt-file-storage-s3
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@aidalinfo/nuxt-file-storage-s3'],
  fileStorage: {
    mount: './uploads', // local storage
  },
})

Configuration

Local storage

fileStorage: {
  mount: process.env.mount || './uploads',
}

S3 / S3-compatible (recommended multi-bucket mode)

fileStorage: {
  s3: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '',
    region: process.env.AWS_REGION || 'us-east-1',
    defaultBucketName: 'private',
    buckets: {
      private: { bucket: process.env.AWS_S3_PRIVATE_BUCKET || 'my-private-bucket' },
      public: { bucket: process.env.AWS_S3_PUBLIC_BUCKET || 'my-public-bucket' },
    },
    endpoint: process.env.AWS_ENDPOINT,  // optional, for S3-compatible services
    forcePathStyle: true,                // required for most S3-compatible services
  },
}

Bucket resolution order is: explicit bucketName argument -> defaultBucketName -> legacy s3.bucket. defaultBucketName is a logical name (private / public) and is used automatically when you do not pass an explicit bucketName to server utils.

Legacy single-bucket mode is still supported:

fileStorage: {
  s3: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '',
    region: process.env.AWS_REGION || 'us-east-1',
    bucket: process.env.AWS_S3_BUCKET || 'my-bucket',
  },
}

If s3 is configured, all server utilities will automatically use S3. Otherwise they fall back to local storage.

Compression (client + server PDF)

The payload contract between frontend and backend stays unchanged (ServerFile[]).

  • Client compression happens before FileReader.readAsDataURL.
  • Server compression (PDF only) happens right before storage (storeFile pipeline), for both local and S3 backends.
fileStorage: {
  compression: {
    client: {
      enabled: true,
      image: {
        enabled: true,
        quality: 0.8,
        maxWidth: 1920,
        maxHeight: 1920,
      },
      pdf: {
        enabled: true,
      },
    },
    server: {
      enabled: true,
      pdf: {
        enabled: true,
      },
    },
  },
}

Legacy flat client configuration is still supported:

fileStorage: {
  compression: {
    enabled: true,
    image: { enabled: true, quality: 0.8, maxWidth: 1920, maxHeight: 1920 },
    pdf: { enabled: true },
  },
}

Option priority is:

  1. handleFileInput(event, override)
  2. useFileStorage(options)
  3. module global config (nuxt.config.ts)
  4. internal defaults

V1 behavior:

  • Images are compressed when enabled.
  • PDF can be optimized server-side with pdf-lib when compression.server is enabled.
  • Non-image / non-PDF files are not processed.
  • Server-side PDF fallback is automatic when optimization fails, output is invalid, or size is not smaller.
  • If client compression fails or is unsupported, upload falls back to original file automatically.
  • No format conversion in V1 (if output MIME differs, original file is used).

Usage

Frontend — useFileStorage()

<script setup>
const { files, handleFileInput } = useFileStorage({ clearOldFiles: true })

const submit = async () => {
  await $fetch('/api/upload', {
    method: 'POST',
    body: { files: files.value },
  })
}
</script>

<template>
  <input type="file" multiple @input="handleFileInput" />
  <button @click="submit">Upload</button>
</template>

Per-upload override example:

<script setup>
const { handleFileInput } = useFileStorage({
  compression: { enabled: true },
})

const onInputNoCompression = (event) => {
  return handleFileInput(event, {
    compression: { enabled: false },
  })
}
</script>

<template>
  <input type="file" @input="onInputNoCompression" />
</template>

Multiple file inputs: create a new instance per input.

<script setup>
const { handleFileInput: handleAvatar, files: avatar } = useFileStorage()
const { handleFileInput: handleBanner, files: banner } = useFileStorage()
</script>

Backend — server utilities

All functions are auto-imported in server routes (server/api/, server/routes/…).

Unified functions (recommended)

Automatically use S3 or local storage based on your config.

// server/api/upload.post.ts
import type { ServerFile } from '@aidalinfo/nuxt-file-storage-s3'

export default defineEventHandler(async (event) => {
  const { files } = await readBody<{ files: ServerFile[] }>(event)

  const keys = await Promise.all(
    files.map(file => storeFile(file, 12, 'uploads'))
  )

  return keys
})

| Function | Description | |---|---| | storeFile(file, name, folder?, bucketName?) | Store a file. name can be a string (fixed name) or number (random ID length). Returns the file key. | | getFile(key, expiresIn?, bucketName?) | Returns a signed URL (S3) or local path. expiresIn defaults to 3600s. | | listFiles(folder?, bucketName?) | List all files in a folder / S3 prefix. | | removeFile(key, bucketName?) | Delete a file. |

Example with explicit logical bucket:

await storeFile(file, 12, 'avatars', 'public')

S3-specific functions

storeFileToS3(file, name, folder?, bucketName?)     // → key
getFileFromS3(key, expiresIn?, bucketName?)         // → signed URL
getFileStreamFromS3(key, bucketName?)               // → ReadableStream
listFilesFromS3(prefix?, maxKeys?, bucketName?)     // → string[]
deleteFileFromS3(key, bucketName?)

Local-specific functions

storeFileLocally(file, name, folder?)  // → filename
getFileLocally(filename, folder?)      // → path
getFilesLocally(folder?)               // → string[]
deleteFile(filename, folder?)

Local dev with Garage (S3-compatible)

Garage is a lightweight self-hosted S3-compatible storage server, great for local development.

A docker-compose.yml is included at the root of this repo. Start it with:

docker compose up -d
./docker/init-garage.sh

The init script creates a bucket and an access key, and prints the credentials to use in your .env:

AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=garage
AWS_S3_PRIVATE_BUCKET=my-private-bucket
AWS_S3_PUBLIC_BUCKET=my-public-bucket
# Legacy mode:
# AWS_S3_BUCKET=my-bucket
AWS_ENDPOINT=http://localhost:3900

A web UI (s3manager) is also available at http://localhost:8080.


Contribution

Run into a problem? Open a new issue.

Want to add a feature? PRs are welcome!

git clone https://github.com/aidalinfo/nuxt-file-storage-s3 && cd nuxt-file-storage-s3
npm i
npm run dev:prepare
npm run dev