Optimize images as they upload — one middleware, zero extra steps

On‑the‑fly WebP/AVIF/JPEG/PNG in Express. Trustworthy, solid, and lightweight.

npm version npm downloads MIT License

Add it once and stop worrying about image optimization and edge cases — forever.

const { upflyUpload, upflyConvert } = require('upfly')
 upflyUpload({
   fields: { 
     'images': { output: 'memory', format: 'webp', quality: 70 },  //webp version stored in req.files
     'HeroImage': { output: 'disk', format: 'webp', quality: 80 }, //webp version saved to disk
     'documents': { output: 'disk'}    //saved to disk as-is,
   },
   outputDir: './uploads',
   limit: 5 * 1024 * 1024 // 5MB
 })
🛡️ Trustworthy
Safe path handling, one‑time dev warnings, graceful fallbacks. Your app stays stable even when conversion fails.
🧱 Solid
Battle‑tested patterns. Clear APIs, typed options, and sensible defaults.
🧠 Memory‑smart
Small files stay in memory. Large files auto‑spill to a temp file and stream through Sharp — lower peak RAM, same API.

Minimal Config — Just Choose Fields

Only pass field names and (optionally) an output directory. Everything else is handled for you.

✨ Just fields

Defaults: format webp · quality 80 · output memory

upflyUpload({
  fields: {
    images: {},
    avatars: {}
  }
})

📂 Fields + Directory

When any field outputs to disk, files are saved safely under your project

upflyUpload({
  fields: { images: { output: 'disk' } },
  outputDir: '/uploads' // resolves to <projectRoot>/uploads
})

Under the hood when you only provide field names:

  • format → webp
  • quality → 80
  • output → memory
  • mimetype is updated (e.g., image/webp)
  • unknown file fields are ignored up front (not buffered)

Two APIs — use what fits your flow

Upload + convert in one step, or keep full control over Multer and convert only

upflyUpload (upload + convert)
One middleware handles memory upload and image conversion. Save to memory or disk.

{ upflyUpload } = require('upfly')

app.post('/upload', upflyUpload({
  fields: { 
    'images': { output: 'memory', format: 'webp', quality: 70 },  //webp version stored in req.files
    'HeroImage': { output: 'disk', format: 'webp', quality: 80 }, //webp version saved to disk
    'documents': { output: 'disk'}    //saved to disk as-is,
  },
  outputDir: './uploads'
}), (req, res) => res.json({ files: req.files }))


upflyConvert (convert only)
Already manage upload yourself? Add conversion only.
const multer = require('multer')
{ upflyConvert } = require('upfly')
const upload = multer({ storage: multer.memoryStorage() })

app.post('/convert',
  upload.fields([{ name: 'images', maxCount: 10 }]), // your upload logic
  upflyConvert({ 
    fields: { 
      images: { format: 'webp', quality: 80 }    //default saves to the memory(req.file || req.files)
    }}),
  (req, res) => res.json({ files: req.files }))

How it works

1
Smart intake
Small files stay in memory. Large files automatically spill to a temp file for minimal RAM.
2
Convert on‑the‑fly
Images are converted to your chosen format and quality using Sharp.
3
Return buffers or save to disk
Keep converted buffers in memory or save directly to a safe output directory.

The Problem Every Developer Faces

Time-Consuming Workflow

You have to manually convert images to WebP before uploading, or set up complex pipelines to handle optimization after upload.

🐌

Slow Website Performance

Large unoptimized images kill your site speed. Users bounce, SEO suffers, and conversion rates drop.

🔧

Complex Setup

Setting up Multer, Sharp, file handling, error management, and different output formats requires tons of boilerplate code.

Upfly solves all of this in one line

Upload files and convert images instantly. Memory or disk storage. Multiple formats. Graceful fallbacks. Type-safe. Production-ready.

Why Developers Love Upfly

Instant Conversion

Convert images to WebP, AVIF, JPEG, or PNG on-the-fly during upload. No separate processing step needed.

🛡️

Bulletproof Error Handling

If conversion fails, files pass through unchanged. Your app never breaks, users never get stuck.

🎯

Memory or Disk

Store converted buffers in memory for immediate use, or save directly to disk. Your choice, seamlessly handled.

📦

One Middleware

Replace dozens of lines of Multer + Sharp boilerplate with one clean middleware configuration.

🔒

Safe Path Handling

Automatically resolves paths safely under your project root. Warns about potential security issues in development.

📝

Fully Typed

Ships with TypeScript definitions. Get autocomplete and type safety out of the box.

Get Started in 30 Seconds

Install upfly and multer (peer dependency)

npm i upfly multer
for npm users
yarn add upfly multer
for Yarn users
pnpm add upfly multer
for pnpm users

Tip: All commands work on Windows, macOS, and Linux — choose the one that matches your package manager.
Note about multer: It's a peer dependency, so you have full control over its version. This prevents conflicts with any existing multer version in your project.
Developer logs: set 'NODE_ENV=development' in your .env file to print conversion details in your terminal.

Configure It Your Way

Here's what YOU write - simple, clean, powerful

💾 Save to Memory

Perfect for cloud uploads or immediate processing

upflyUpload({
  fields: {
    profilePic: { output: 'memory', format: 'webp', quality: 85 },
    gallery:    { output: 'memory', format: 'avif', quality: 75 }
  }
})

💿 Save to Disk

Great for static file serving and CDN uploads

upflyUpload({
  fields: {
    images:    { output: 'disk', format: 'webp', quality: 80 },
    documents: { output: 'disk' } // Non-images pass through
  },
  outputDir: './uploads'
})

🎨 Mixed Scenarios

Different fields, different formats, different outputs

upflyUpload({
  fields: {
    thumbnails: { output: 'memory', format: 'webp', quality: 60 },
    originals:  { output: 'disk', format: 'jpeg', quality: 95 },
    avatars:    { output: 'disk', format: 'avif', quality: 70 }
  },
  outputDir: './public/uploads',
  limit: 10 * 1024 * 1024 // 10MB
})

⚙️ Conversion Only

Use with your own upload setup

// Your existing upload
 const upload = multer({ storage: multer.memoryStorage() });

// Just add conversion
upflyConvert({ fields: { images: { format: 'webp', quality: 80 } } })

Complete API Example

Here's how it looks in a real Express route

const express = require('express');
const { upflyUpload } = require('upfly');


const app = express();

// Single route handles upload + conversion
app.post('/upload',
  upflyUpload({
    fields: {
      images:    { output: 'memory', format: 'webp', quality: 80 },
      documents: { output: 'disk' } // PDFs, etc. saved as-is
    },
    outputDir: './uploads',
    limit: 5 * 1024 * 1024 // 5MB limit
  }),
  (req, res) => {
    // Images are now optimized WebP in req.files.images
    // Documents are saved to disk in req.files.documents
    
    res.json({ message: 'Upload successful!', files: req.files });
  }
);

app.listen(3000);

Handles Every Edge Case

🖼️ Non-Image Files

PDFs, documents, videos pass through unchanged. Only images get converted. No errors, no confusion.

🔧 Conversion Failures

If Sharp fails (corrupted image, unsupported format), original file is returned. Your app keeps running.

🛡️ Path Security

Paths like '/uploads' resolve safely under your project root. Warns about potential issues in development.

📁 Auto Directory Creation

Output directories are created automatically. No need to manually create folder structures.

🏷️ Smart Filenames

Generated filenames include field names, slugified originals, and unique suffixes. No collisions, ever.

⚡ Performance Logging

Development mode shows before/after file sizes and conversion details. Know exactly what's happening.

Just use "NODE_ENV=development" in your .env file