The storage client provides a full-featured S3-compatible API that works with both AWS S3 (production) and MinIO (local dev). It auto-detects the environment.
Upload a picture shrunk first with optimizeImage() — never upscaled, same format, original bytes when re-encoding is not smaller. Content type is the sniffed format’s; x-amz-meta-original-bytes records the input size. PutImageOptions = put options + maxDimension, jpegQuality, webpQuality, avifQuality, stripMetadata. Needs the optional sharp peer
get(key, options?)
Download data as Buffer
getStream(key, options?)
Download data as readable stream
head(key)
Get object metadata without downloading
delete(key)
Delete an object
deleteMany(keys)
Delete multiple objects
copy(src, dest, options?)
Copy an object
move(src, dest, options?)
Move an object (copy + delete)
list(options?)
List objects (paginated)
listAll(options?)
List all objects (auto-paginate)
exists(key)
Check if an object exists
getSignedUrl(key, options?)
Get a signed download URL
getSignedUploadUrl(key, options?)
Get a signed upload (PUT) URL. Throws if maxSize is passed — a presigned PUT signs a request, not a policy, so S3 cannot cap the size; use presignUploadPost
A signed POST form (PresignedPostUpload{ url, fields, expiresAt }) whose policy S3 evaluates itself: content-length-range caps the object at maxSize bytes (minSize default 1), and the exact key and content type are pinned, so a leaked form cannot be repurposed. Signed locally with the client’s own credentials — no extra call, no extra dependency. expiresIn default 3600, max 604 800
getPublicUrl(key, options?)
The object’s public URL (CDN domain when configured)
for (const [k, v] of Object.entries(fields)) form.append(k, v);
form.append('file', file);
awaitfetch(url, { method: 'POST', body: form }); // 204 on success, 4xx when the policy fails
buildPresignedPost(input) is the pure SigV4 POST-policy builder behind it (BuildPresignedPostInput → PresignedPostForm), exported for custom endpoints or tests.
Uploads are untrusted input: the image format is sniffed from the first bytes, never taken from the filename or declared content type.
Function
Description
optimizeImage(input, options?)
Shrink a picture once, on the way in. Three guarantees: only reduce (never upscale; if the re-encode is not smaller the input comes back byte-for-byte), same format in → same format out (PNG stays PNG, animated GIF stays animated), sniffed not trusted. JPEG, PNG, WebP, GIF, AVIF, TIFF are re-encoded; SVG, BMP, ICO, HEIC/HEIF and unknown formats pass through with a reason. Returns OptimizedImage — body, contentType, format, outcome ('reduced' | 'kept' | 'passthrough'), originalBytes, bytes, width?, height?. Options: maxDimension, jpegQuality, webpQuality, avifQuality, stripMetadata (default true — drops EXIF)