GridFS uploads in NestJS: the file sits in memory first

In dkhoa-dev/nestjs-multer-gridfs, downloads stream but uploads buffer the whole file. Here is where, and what to change.

Phan Minh Dang Khoa
3 min readAI draft · AI reviewed
Contents

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)
Where the upload sits in memory before GridFS sees it.

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.ts connects to mongodb://localhost:62553/fs. The two do not match.
  • downloadFile and deleteFile wrap everything in try/catch and throw NotFoundException. 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.fileSize on 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.
Phan Minh Dang Khoa
Principal Engineer · AI, IoT and industrial automation.
Next post →Point-adjusted F1 flatters anomaly detectors: notes on TSB-AD