← Dev log

backend

Multer, and streaming straight through to storage

Multer, and streaming straight through to storage

Multer is the Express/NestJS middleware for parsing multipart/form-data — the format browsers use for file uploads. Without it there's no built-in way to pull a file out of a form submission.

@Post("upload")
@UseInterceptors(FileInterceptor("file"))
uploadDocument(@UploadedFile() file: Express.Multer.File) {
  // file.buffer, file.mimetype, file.size, file.originalname
}

Memory storage, not disk. For anything that forwards straight to object storage, the buffer goes into the storage call directly — nothing touches the container's disk, nothing to clean up, no risk of the disk filling under upload traffic.

Limits belong at the Multer layer, not checked after the fact:

MulterModule.register({
  limits: { fileSize: maxFileSizeBytes },
  fileFilter: (req, file, cb) => cb(null, allowedMimeTypes.includes(file.mimetype)),
});

This is both UX — fail fast, before an upload attempt — and abuse prevention. Without a size limit, a client can try to upload something enormous and eat server memory and bandwidth before anything rejects it. I keep max size and allowed MIME types configurable per platform tier rather than hardcoded, for exactly that reason.

Never trust the client-declared MIME type alone. A client can rename a script to .jpg and claim image/jpeg. For anything security-sensitive — served back publicly, or run through a thumbnail generator — sniff the actual content server-side rather than trusting Content-Type, especially before processing it.

Multer's job stops at getting usable bytes out of the request. The flow it feeds: size and type limits validate the file, it streams to storage, a documents row records the storage key, checksum and MIME type, and the thumbnail service optionally picks it up. Everything after Multer is the app's own pipeline.