Skip to content

@venturekit/storage API

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.

Create a storage client with explicit configuration.

import { createStorageClient } from '@venturekit/storage';
const storage = createStorageClient({
bucket: 'my-uploads',
prefix: 'tenant-123/', // optional key prefix
});

Config options:

OptionTypeDescription
bucketstringS3 bucket name (required)
regionstringAWS region (default: AWS_REGION env var)
endpointstringCustom S3 endpoint (auto-set for MinIO locally)
forcePathStylebooleanForce path-style URLs (auto-set for MinIO)
prefixstringKey prefix for all operations
credentialsobjectAWS credentials (uses default chain if omitted)

Create a storage client from environment variables. Reads VENTURE_STORAGE_BUCKET, VENTURE_STORAGE_PREFIX, and MinIO config from env.

import { createStorageClientFromEnv } from '@venturekit/storage';
const storage = createStorageClientFromEnv();
MethodDescription
put(key, body, options?)Upload data (Buffer, string, or stream)
putImage(key, body, options?)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
presignUpload(key, options?) / presignDownload(key, options?)Same as the two above, returning PresignedUrl { url, expiresAt }
presignUploadPost(key, { maxSize, minSize?, contentType?, expiresIn?, metadata? })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)
upload(key, body, options?)Multipart upload for large files
// API: mint the form
const { url, fields, expiresAt } = await storage.presignUploadPost(`avatars/${userId}.jpg`, {
contentType: 'image/jpeg',
maxSize: 5 * 1024 * 1024,
expiresIn: 600,
});
// Browser: submit fields in order, the file LAST
const form = new FormData();
for (const [k, v] of Object.entries(fields)) form.append(k, v);
form.append('file', file);
await fetch(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.

OptionTypeDescription
contentTypestringMIME content type
cacheControlstringCache control header
metadataRecord<string, string>Custom metadata
storageClassstringStorage class (STANDARD, GLACIER, etc.)
encryption'AES256' | 'aws:kms'Server-side encryption
tagsRecord<string, string>Object tags
OptionTypeDescription
expiresInnumberExpiration in seconds (default: 3600)
responseContentTypestringOverride response content type
responseContentDispositionstringOverride content disposition

Uploads are untrusted input: the image format is sniffed from the first bytes, never taken from the filename or declared content type.

FunctionDescription
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)
sniffImageFormat(buf)ImageFormat ('jpeg' | 'png' | 'webp' | 'gif' | 'avif' | 'tiff' | 'heif' | 'svg' | 'bmp' | 'ico') or null
sanitizeFilename(name)Lower-cased, runs of non-[a-z0-9._-] collapsed to -, separators trimmed, capped at 200 chars — for the trailing segment of an object key

sharp (libvips) is loaded lazily and declared as an optional peer, so a project that never stores images carries no native binary.

FunctionDescription
createS3Config(options)Create S3 bucket configuration
buildS3Config(config)Build S3 infrastructure config
ConstantDescription
DEFAULT_S3_CONFIGDefault S3 configuration
TypeDescription
StorageClientThe storage client interface
StorageClientConfigClient configuration
PutOptionsUpload options
GetResultDownload result (body + metadata)
GetStreamResultStream download result
HeadResultMetadata-only result
ListResultList result with pagination
ObjectInfoObject metadata in list results
SignedUrlOptionsSigned download URL options
SignedUploadUrlOptionsSigned upload URL options
S3OutputsS3 deployment outputs
S3InfraConfigS3 infrastructure configuration