@ebowwa/object-store
v0.3.0
Published
Provider-agnostic S3 object storage abstraction (Hetzner, R2, GCS, AWS, memory)
Maintainers
Readme
@ebowwa/object-store
Provider-agnostic S3-compatible object storage abstraction. Works with Cloudflare R2, Hetzner Storage Boxes, AWS S3, GCS, and an in-memory provider for tests.
Features
- Unified
ObjectStoreinterface — swap providers without changing consumer code - Multipart uploads — automatic managed multipart for streams and large buffers (≥5MB) via
@aws-sdk/lib-storage - Pre-signed URLs — both GET and PUT for temporary direct access
- Batch operations — delete up to 1000 keys per request
- Memory provider — zero-dep in-memory implementation for testing
Usage
import { createObjectStore } from "@ebowwa/object-store";
// Auto-configured from environment variables
const store = createObjectStore();
await store.put("sessions/abc123/video/seg-0001.mjpeg", buffer);
const data = await store.get("sessions/abc123/video/seg-0001.mjpeg");
const url = await store.signedUrl("sessions/abc123/video/seg-0001.mjpeg", 3600);Environment Variables
| Variable | Description |
|----------|-------------|
| S3_ENDPOINT | Provider endpoint URL |
| S3_BUCKET | Bucket name |
| S3_ACCESS_KEY | Access key ID |
| S3_SECRET_KEY | Secret access key |
| S3_REGION | Region (default: auto) |
| S3_FORCE_PATH_STYLE | Set true for MinIO/Hetzner |
ObjectStore Interface
interface ObjectStore {
put(key: string, data: Buffer | ReadableStream, meta?: Record<string, string>): Promise<void>;
get(key: string): Promise<Buffer | null>;
delete(key: string): Promise<void>;
list(prefix?: string): Promise<string[]>;
exists(key: string): Promise<boolean>;
signedUrl(key: string, ttlSeconds: number): Promise<string>;
head(key: string): Promise<ObjectMeta | null>;
listPage(prefix: string | undefined, opts?: { cursor?: string; limit?: number }): Promise<ListPageResult>;
deleteBatch(keys: string[]): Promise<DeleteBatchResult>;
copy(srcKey: string, dstKey: string): Promise<void>;
move(srcKey: string, dstKey: string): Promise<void>;
size(prefix?: string): Promise<number>;
count(prefix?: string): Promise<number>;
signedPutUrl(key: string, ttlSeconds: number, opts?: { contentType?: string }): Promise<string>;
}