X Enterprises

fastify-xstorage

S3-compatible file storage plugin for Fastify — upload, download, delete, copy, move, list, signed URLs, presigned upload URLs, and batch operations for AWS S3, Cloudflare R2, and DigitalOcean Spaces.

fastify-xstorage

S3-compatible file storage for Fastify v5. Register once and get fastify.xStorage — a full-featured decorator covering upload, download, delete, copy, move, list, existence checks, metadata, signed download URLs, and presigned upload URLs. Works with AWS S3, Cloudflare R2, DigitalOcean Spaces, and any S3-compatible provider.

Installation

npm install @xenterprises/fastify-xstorage

For HTTP file uploads, also install:

npm install @fastify/multipart@9

Quick Start

import Fastify from "fastify";
import xStorage from "@xenterprises/fastify-xstorage";

const fastify = Fastify({ logger: true });

await fastify.register(xStorage, {
  accessKeyId: process.env.STORAGE_ACCESS_KEY_ID,
  secretAccessKey: process.env.STORAGE_SECRET_ACCESS_KEY,
  bucket: process.env.STORAGE_BUCKET,
  publicUrl: process.env.STORAGE_PUBLIC_URL,
  endpoint: process.env.STORAGE_ENDPOINT, // omit for AWS S3
});

// Upload a file
const result = await fastify.xStorage.upload(buffer, "photo.jpg", {
  folder: "avatars",
});
// { key: "avatars/photo-a1b2c3d4.jpg", url: "https://cdn.example.com/...", ... }

// Signed download URL (1 hour)
const url = await fastify.xStorage.getSignedUrl(result.key, 3600);

Options

NameTypeDefaultRequiredDescription
accessKeyIdstringYesStorage access key ID
secretAccessKeystringYesStorage secret access key
bucketstringYesBucket name
publicUrlstringYesBase public URL for the bucket (trailing slash trimmed automatically)
endpointstringNoCustom endpoint URL for R2/DO Spaces/other providers; omit for AWS S3
regionstring"us-east-1"NoAWS region or "auto" for Cloudflare R2
forcePathStylebooleantrueNoUse path-style S3 URLs (required by most non-AWS providers)
aclstring"private"NoDefault ACL applied to uploads

Methods

Upload

Download

Delete

Copy & Move

Metadata & Existence

  • exists(key) — Check whether a file exists without downloading it.
  • getMetadata(key) — Get content type, size, last-modified, ETag, and custom metadata.

List

URLs

Error Reference

ErrorCause
[xStorage] accessKeyId is required and must be a stringMissing or non-string accessKeyId at registration
[xStorage] secretAccessKey is required and must be a stringMissing or non-string secretAccessKey
[xStorage] bucket is required and must be a stringMissing or non-string bucket
[xStorage] publicUrl is required and must be a stringMissing or non-string publicUrl
[xStorage] endpoint must be a string when providedNon-string endpoint
[xStorage] file is required for uploadupload() called without a file
[xStorage] filename is required and must be a stringMissing or non-string filename
[xStorage] files must be a non-empty arrayuploadMultiple() given empty or non-array
[xStorage] keys must be a non-empty array of stringsdeleteMultiple() given empty or non-array
[xStorage] key is required and must be a stringAny method called without a valid key
[xStorage] expiresIn must be a positive number (seconds)Non-positive expiresIn
[xStorage] Failed to upload file "<key>": <reason>S3 API error during upload
[xStorage] Failed to download file "<key>": <reason>S3 API error during download
[xStorage] Failed to delete file "<key>": <reason>S3 API error on single delete
[xStorage] Failed to delete multiple files: <reason>S3 API error on batch delete
[xStorage] Failed to copy "<src>" to "<dst>": <reason>S3 API error on copy
[xStorage] Failed to list files with prefix "<prefix>": <reason>S3 API error on list
[xStorage] Failed to get metadata for "<key>": <reason>HeadObject error
[xStorage] Failed to generate signed URL for "<key>": <reason>Pre-signer error on download URL
[xStorage] Failed to generate signed upload URL for "<key>": <reason>Pre-signer error on upload URL

Environment Variables

VariableRequiredDescription
STORAGE_ACCESS_KEY_IDYesStorage access key ID
STORAGE_SECRET_ACCESS_KEYYesStorage secret access key
STORAGE_BUCKETYesBucket name
STORAGE_PUBLIC_URLYesBase public URL for the bucket
STORAGE_ENDPOINTNoCustom endpoint for R2/DO Spaces/other providers
STORAGE_REGIONNoAWS region (default "us-east-1")

How It Works

On registration the plugin validates all required options, creates an S3Client with the provided credentials and optional custom endpoint, then decorates fastify.xStorage with all storage methods. The publicUrl trailing slash is normalised once at startup. Uploaded files get a random 8-byte hex suffix by default to avoid collisions (useRandomName: false disables this). MIME types are auto-detected from the filename extension via mime-types. list() returns a single page up to maxKeys; listAll() follows ContinuationToken until all results are fetched. move() is copy-then-delete. exists() uses HeadObject and returns false on 404 without throwing. getSignedUploadUrl() generates a PutObject presigned URL so clients can upload directly to storage without routing file payloads through your Fastify server.

AI Context

package: "@xenterprises/fastify-xstorage"
type: fastify-plugin
use-when: File storage with AWS S3, Cloudflare R2, or DigitalOcean Spaces — upload, download, delete, copy/move, list, signed URLs
decorator: fastify.xStorage
env: STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY, STORAGE_BUCKET, STORAGE_PUBLIC_URL, STORAGE_ENDPOINT (optional), STORAGE_REGION (optional)
requires: accessKeyId, secretAccessKey, bucket, publicUrl at registration
Copyright © 2026