Mongodb Gridfs is it good for production application

Hello I have a setup with documents and file uploads in the same db, so files are just pdfs like 3-4 mb each document has a file refrence to it
I just want to know if its a good idea to use both, since mongodb has a native support for that?
can I cause performance blockers?

Hey Pranit! Welcome to the community… With GridFS… It’s definitely technically an option, and yeah, it’s native to MongoDB which makes it feel convenient, especially when you’re already storing related documents. That said… it’s not the most scalable or modern solution for storing files in a production setup.

Here’s why you might want to rethink it:

  • Performance: GridFS chunks and stores files in the DB, which means bigger docs, increased I/O, and potential performance bottlenecks under load.
  • Cost and scaling: Object storage services like Amazon S3, Google Cloud Storage, or Azure Blob are purpose-built for this — cheaper, faster, and easier to scale.
  • Separation of concerns: Files in object storage, metadata in MongoDB = cleaner architecture and better maintainability.

If you’re sticking to 3–4MB PDFs and low traffic, GridFS will work. But if you’re planning to scale or optimize down the road… I’d def recommend using object storage and keeping MongoDB for what it does best: document data… use MongoDB to store the meta data associated with your pdf’s and store a reference to the object store for the blob.

Let us know what framework or stack you’re using maybe we can point you to some good examples or starter kits?

Hey Michael, thank you for replying so quickly.
I am having Next js with node and using a self hosted mongodb solution, there were inital I/O concerns but I am not sure if it will happen at query level?
My client has a specifc requirement of self hosting because of data privacy issue.
PDF sizes will stay around 3-4MB but with High traffic.
Down the road, yes scaling is required but it will be at at document level rather than the grid.

Well great… I’m a MASSIVE Next.js fan… I’ve been using it exclusively for all my recent projects. Since you’re self-hosted… what if you were to leverage a filesystem store for your pdf’s…

{
  "_id": ObjectId("..."),
  "title": "title of your PDF",
  "filePath": "/uploads/your.pdf",
  "mimeType": "application/pdf",
  "uploadedAt": ISODate("2025-04-01T10:00:00Z")
}

I’m using Multer for file uploads.

I’d build a project structure something like this:

your_app/
├── pages/
│   └── api/
│       ├── upload.js
│       └── file/[id].js
├── uploads/
├── lib/
│   └── mongodb.js

Basic MongoDB Connection:

import { MongoClient } from 'mongodb';

const uri = process.env.MONGODB_URI;
const client = new MongoClient(uri);

export async function connectToDatabase() {
  if (!client.isConnected()) await client.connect();
  const db = client.db(); // or specify your specific db name
  return { db, client };
}

Then create an api upload:

import nextConnect from 'next-connect';
import multer from 'multer';
import { connectToDatabase } from '../../lib/mongodb';
import path from 'path';
import fs from 'fs';

const uploadDir = './uploads';
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir);

const upload = multer({ dest: uploadDir });

const apiRoute = nextConnect({
  onError(error, req, res) {
    res.status(501).json({ error: `Upload failed: ${error.message}` });
  },
  onNoMatch(req, res) {
    res.status(405).json({ error: `Method ${req.method} not allowed` });
  },
});

apiRoute.use(upload.single('file'));

apiRoute.post(async (req, res) => {
  const { db } = await connectToDatabase();
  const { originalname, filename, mimetype, path: filepath } = req.file;

  const result = await db.collection('files').insertOne({
    originalname,
    filename,
    mimetype,
    filepath,
    uploadedAt: new Date()
  });

  res.status(200).json({ success: true, id: result.insertedId });
});

export default apiRoute;
export const config = { api: { bodyParser: false } };

and maybe a file server:

import { connectToDatabase } from '../../../lib/mongodb';
import { ObjectId } from 'mongodb';
import fs from 'fs';
import path from 'path';

export default async function handler(req, res) {
  const {
    query: { id },
  } = req;

  const { db } = await connectToDatabase();
  const fileDoc = await db.collection('files').findOne({ _id: new ObjectId(id) });

  if (!fileDoc) {
    res.status(404).json({ error: 'File not found' });
    return;
  }

  const filePath = path.join(process.cwd(), fileDoc.filepath);
  const fileStream = fs.createReadStream(filePath);

  res.setHeader('Content-Type', fileDoc.mimetype);
  fileStream.pipe(res);
}

You could use a simple form in your UI:

const uploadFile = async (event) => {
  const formData = new FormData();
  formData.append("file", event.target.files[0]);

  const res = await fetch("/api/upload", {
    method: "POST",
    body: formData,
  });

  const data = await res.json();
  console.log("Uploaded File ID:", data.id);
};

Hope this helps! Let us know how you make out.

Regards,
Mike
https://mlynn.org