← Dev log

learning

The storage abstraction mattered more than S3

The storage abstraction mattered more than S3

Documents, photos, gallery images, staff photos — a real volume of user-uploaded files. Storing those on the app server's disk creates real problems at scale: disk fills up, backups get huge and slow, horizontal scaling means every instance needs access to the same files, and a server failure risks losing them unless disk-level backups are perfect. Object storage solves all of it — effectively unlimited, durable by design, naturally shared across every backend instance.

Two pieces: client-s3 is the SDK for uploading, downloading and deleting. s3-request-presigner generates time-limited signed URLs that let a client upload or download directly to or from storage, with the file never passing through the backend.

const command = new PutObjectCommand({ Bucket, Key: storageKey });
const uploadUrl = await getSignedUrl(s3Client, command, { expiresIn: 900 });

This matters most for large files — routing a big upload through the backend makes that server's bandwidth and memory the bottleneck for every upload. A presigned URL lets the client talk directly to storage; the backend's only job is generating the signed permission slip.

The abstraction is the actual lesson

storage_provider is a plain string column, and core/storage/ wraps all storage access behind an interface rather than every module calling the SDK directly. That's what pays off — if the storage backend ever changes (a different provider, a local/on-prem requirement for compliance), that change is contained to one module, not a search-and-replace across every place a file gets touched.

A checksum alongside the storage key lets the system verify integrity independent of trusting the provider, and detect duplicate uploads. metadata_json holds whatever per-file metadata varies by document type, without a new column for every possible attribute.

Documents default to is_private: true — not every uploaded file should be readable via a guessable URL. Access to private documents goes through the backend, checking the requester actually has permission to view this specific document, rather than relying on the URL being merely hard to guess. An unguessable-but-public URL is not access control — and that distinction matters a lot for a system handling people's personal documents.