Skip to Content
✨ v2.0.3 Released - See the release notes
DocsNextmin NodeStorage Adapters

Storage Adapters

Nextmin Node provides a pluggable file storage system. When initializing the createNextMinRouter, you must pass a fileStorageAdapter.

We provide built-in adapters for AWS S3 (and compatible services) and local file system storage.

Local Filesystem (New)

The LocalFileStorageAdapter stores uploaded files in a local directory. This is ideal for development or simple self-hosted deployments.

Usage

ts
import { LocalFileStorageAdapter } from '@airoom/nextmin-node';
import path from 'path';
 
const fileStorageAdapter = new LocalFileStorageAdapter({
  // Directory where files will be saved
  uploadDir: path.join(__dirname, 'public/uploads'),
  // Base URL for accessing the files
  baseUrl: 'http://localhost:3000/uploads',
});
 
const nextminRouter = createNextMinRouter({
  // ... other options
  fileStorageAdapter,
});

S3 (AWS / MinIO)

The S3FileStorageAdapter allows you to store files in any S3-compatible service.

Usage

ts
import { S3FileStorageAdapter } from '@airoom/nextmin-node';
 
const fileStorageAdapter = new S3FileStorageAdapter({
  bucket: process.env.S3_BUCKET,
  region: process.env.S3_REGION,
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY_ID,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  },
  // Optional: Custom endpoint (e.g. for MinIO)
  endpoint: process.env.S3_ENDPOINT,
  forcePathStyle: true,
});

Image Proxying & Transformation (New)

Nextmin can automatically transform storage URLs (like S3/R2 links) into proxied URLs served through your own domain. This is essential for SEO and brand consistency.

How it works

  1. Transformation: When the API returns a document, it scans all fields for URLs matching your storage base and replaces them with your proxy base (e.g., /images).
  2. Proxying: The backend provides a new endpoint GET /rest/files/:key(*) that streams the actual file from storage to the browser.

Automatic Configuration

If you have FRONTEND_URL set in your environment variables, Nextmin will automatically enable a relative proxy at /images.

Manual Configuration

You can also configure this explicitly in your NMAdapter options:

ts
const dbAdapter = new NMAdapter({
  // ... other options
  imageProxy: {
    // The original S3/R2 base URL to look for
    originalBase: 'https://mybucket.s3.us-east-2.amazonaws.com',
    // The new base path to use in API responses
    proxyBase: '/images'
  }
});

Next.js Integration

To make this work seamlessly with Next.js, add a rewrite to your next.config.js:

js
// next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/images/:path*',
        destination: 'https://api.yourdomain.com/rest/files/:path*',
      },
    ]
  },
}

Dynamic Image Resizing & WebP Optimization

The file proxy endpoint GET /rest/files/:key(*) includes high-performance image processing (powered by sharp):

  • Default WebP Format: All image uploads (JPEG, JPG, PNG, WEBP) are automatically served in optimized WebP format (image/webp) by default for fast loading times and reduced bandwidth.
  • Dynamic Resizing: Pass width (w), height (h), quality (q), or format (fmt) query parameters:
    • GET /rest/files/uploads/avatar.jpg?w=300&h=300 -> Resizes to 300x300 WebP.
    • GET /rest/files/uploads/banner.jpg?w=800&q=90 -> Resizes to 800px width with 90% quality.
    • GET /rest/files/uploads/icon.png?fmt=png -> Converts to PNG format.
    • GET /rest/files/uploads/photo.jpg?raw=true -> Serves original raw file without conversion.
  • Disk Caching: Resized images are cached on disk under .nextmin/cache/resized/ with MD5 hashes of transform parameters (key + width + height + quality + format), delivering lightning-fast cache hits (X-Cache: HIT) without invoking image processing pipelines again.
  • Graceful SVG Placeholder Fallback: If an image is not found in storage (404/NoSuchKey), the proxy returns a sleek SVG placeholder (image/svg+xml) with HTTP 200, ensuring UI cards, avatars, and listings never display broken image icon errors.
  • CDN & Browser Caching: All proxied images return long-lived cache headers (Cache-Control: public, max-age=31536000, immutable), ensuring sub-millisecond cached responses via Cloudflare/browser cache.

Original Filenames & Duplicate Prevention

  • Clean Original Names: File uploads retain their original base name (slugified and sanitized, e.g. "Doctor John.jpg" -> "doctor-john.jpg") without appending random hash suffixes.
  • Unicode Support: The slugifier natively supports Unicode letters and characters (\p{L}\p{N}\p{M}), preserving Bengali, Arabic, and other non-Latin scripts.
  • UTF-8 Filename Repair: Automatically repairs Multer’s default Latin1/ISO-8859-1 multipart header decoding, guaranteeing accurate non-ASCII file names.
  • S3 / Cloudflare R2 Metadata Sanitization: Custom upload metadata is automatically scrubbed of non-printable or non-ASCII characters to comply strictly with AWS S3 HTTP header specifications and avoid runtime header exceptions.
  • Duplicate Prevention: If a file with the exact same name already exists in storage, @airoom/nextmin-node rejects the upload with HTTP 409 Conflict:
    json
    {
      "error": true,
      "code": "FILE_EXISTS",
      "message": "File 'doctor-john.jpg' already exists in storage. Please rename the file manually and try again."
    }
Last updated on