The public repo dkhoa-dev/nestjs-multer-gridfs is a small NestJS service that stores files in MongoDB GridFS. It has four routes: POST /files/upload, GET /files, GET /files/:id and DELETE /files/:id. This post looks at one asymmetry in it: downloads stream, uploads do not.
Download streams
downloadFile calls bucket.openDownloadStream(id) and the controller does stream.pipe(res). Chunks go from MongoDB to the client as they arrive. The server never holds the whole file.
Upload buffers
The upload route uses FileInterceptor('file') with no options. Multer’s README says that when you omit the options object, files are kept in memory and never written to disk. So file.buffer holds the entire upload before GridFS sees a byte.
uploadFile then wraps that buffer in a stream and pipes it into bucket.openUploadStream:
const bufferStream = require('stream').Readable.from(file.buffer);
bufferStream.pipe(uploadStream)
GridFS splits the file into chunks of 255 * 1024 bytes by default (chunkSizeBytes in the mongodb driver, 6.12.0 in the lockfile). The chunking is not the problem. The buffer that exists before it is.
Multer also sets no size cap unless you pass limits. Its README lists fileSize with a default of Infinity. This repo passes none, so one large upload holds at least its own size in process memory.
Two smaller findings
- The README says MongoDB runs on port 27017.
src/app.module.tsconnects tomongodb://localhost:62553/fs. The two do not match. downloadFileanddeleteFilewrap everything intry/catchand throwNotFoundException. A malformed id and a real failure both return 404.
Lessons
- A call that looks like a stream (
pipe) can still sit behind a buffer. Check where the bytes come from. - Set
limits.fileSizeon every Multer upload route, even in a demo. - To keep memory flat, hand the multipart stream to GridFS directly instead of using
file.buffer. This repo does not do that yet. - Check the README against the code. Here they disagree on the database port.