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.