Cover
Foto profil

Bembi Pramudya

@beemind

I'm Bembi, also known as beemind, a web developer based in Indonesia. I spend my free time experimenting with AI, diving into networks and privacy-focused web tools, and listening to music

DeveloperIndonesiahaibem.cc
Web

Uploads Without a Filesystem: Storing Files in PostgreSQL

Bembi Pramudya
Bembi PramudyaOct 6, 2026 · 2 min read
Uploads Without a Filesystem: Storing Files in PostgreSQL

The classic problem when deploying to serverless: there is no writable filesystem. On Vercel, the public directory is read-only at runtime — so the common "save to public/uploads" strategy fails in production.

My solution: store files as BYTEA in PostgreSQL. The uploads table has just three columns: name, mime, and data. The /api/upload endpoint accepts multipart/form-data, validates the file type and magic bytes, then INSERTs into the database. Files are served back through /api/files/[name], which reads the BYTEA and returns it with the correct Content-Type.


-- the uploads table: this simple
CREATE TABLE uploads (
  name TEXT PRIMARY KEY,
  mime TEXT NOT NULL,
  data BYTEA NOT NULL
);

Layered validation: first the allowed MIME types (JPG, PNG, GIF, WebP), then the 5MB limit, then magic bytes — because file.type from the browser can be faked. JPEG must start with FF D8 FF, PNG with 89 50 4E 47. Only files passing every check get stored.

The downside is obvious: the database grows and every image request becomes a DB query. At the scale of a personal portfolio blog, the tradeoff is worth it — zero external storage dependencies, backups ride along with the database, and no orphaned files when data is deleted.