Skip to content

16 File Upload

DodaTech 4 min read

title: File Upload in Node.js REST APIs — Complete Guide weight: 26 date: 2026-06-28 lastmod: 2026-06-28 description: Implement file upload in Node.js REST APIs using Multer with single/multiple files, file validation, size limits, image processing, and cloud storage integration. tags: [api-development, nodejs]


File upload in Node.js REST APIs uses the Multer middleware to handle multipart/form-data, supporting single file uploads, multiple files, file type validation, size limits, and integration with cloud storage services.

```mermaid
flowchart TD
  A[Client Upload] --> B[Multer Middleware]
  B --> C{Validation}
  C -->|Valid| D[Save File]
  C -->|Invalid| E[Error Response]
  D --> F[Local Storage / Cloud]
  F --> G[Return File URL]
  style A fill:#e1f5fe
  style B fill:#fff9c4
  style C fill:#c8e6c9
  style E fill:#ffcdd2

Multer reads multipart/form-data and makes files available as req.file or req.files. Configure storage (disk or memory), file filter (allowed types), and size limits. For production, upload to cloud storage (S3, Cloudinary) and store only the URL in your database.

Think of Multer like a mail room clerk. The clerk receives packages (files), checks they meet requirements (type, size), stamps them (metadata), and places them in the appropriate storage (disk or cloud).

Example: Basic File Upload Configuration

const multer = require('multer');
const path = require('path');

// Storage configuration
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, uniqueSuffix + path.extname(file.originalname));
  }
});

// File filter
const fileFilter = (req, file, cb) => {
  const allowed = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
  if (allowed.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error('Invalid file type. Only JPEG, PNG, GIF, and PDF allowed.'), false);
  }
};

const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
  fileFilter
});

// Single file upload
app.post('/api/upload/avatar', upload.single('avatar'), (req, res) => {
  res.json({
    message: 'File uploaded',
    file: req.file.filename,
    size: req.file.size,
    mimetype: req.file.mimetype
  });
});

Example: Multiple File Upload

// Multiple files (same field name)
app.post('/api/upload/gallery', upload.array('images', 5), (req, res) => {
  const files = req.files.map(f => ({
    filename: f.filename,
    size: f.size,
    mimetype: f.mimetype
  }));
  res.json({ message: `${files.length} files uploaded`, files });
});

// Multiple fields
const cpUpload = upload.fields([
  { name: 'avatar', maxCount: 1 },
  { name: 'gallery', maxCount: 5 }
]);

app.post('/api/upload/profile', cpUpload, (req, res) => {
  console.log('Avatar:', req.files['avatar'][0].filename);
  console.log('Gallery:', req.files['gallery'].map(f => f.filename));
  res.json({ message: 'Profile upload complete' });
});

Example: Cloud Storage Upload (S3)

const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const multer = require('multer');
const multerS3 = require('multer-s3');

const s3 = new S3Client({
  region: process.env.AWS_REGION,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY,
    secretAccessKey: process.env.AWS_SECRET_KEY
  }
});

const uploadS3 = multer({
  storage: multerS3({
    s3,
    bucket: 'myapp-uploads',
    acl: 'public-read',
    metadata: (req, file, cb) => {
      cb(null, { fieldName: file.fieldname });
    },
    key: (req, file, cb) => {
      const uniqueName = `${Date.now()}-${file.originalname}`;
      cb(null, `avatars/${uniqueName}`);
    }
  }),
  limits: { fileSize: 5 * 1024 * 1024 }
});

app.post('/api/upload/s3', uploadS3.single('file'), (req, res) => {
  res.json({
    message: 'Uploaded to S3',
    url: req.file.location,
    key: req.file.key
  });
});

Common Mistakes

  1. Not validating file types on the server — Relying on client-side validation alone allows malicious files. Always validate MIME type and file extension on the server.
  2. Allowing unlimited file sizes — Large files can exhaust disk space and memory. Set reasonable size limits based on your use case.
  3. Saving files with user-provided names — Original filenames may contain path traversal characters (../). Generate unique filenames server-side.
  4. Not cleaning up failed uploads — If upload starts but fails validation mid-stream, the partial file wastes space. Use temporary directories and cleanup routines.
  5. Storing files on application server in production — Application servers should be stateless. Use cloud storage (S3, Cloudinary) and delete local files after upload.

Practice Questions

  1. What middleware handles file uploads in Express?
  2. How do you validate file types with Multer?
  3. What is the difference between single, array, and fields upload methods?
  4. Why should you use cloud storage instead of local storage in production?
  5. Challenge: Build a file upload system that supports avatar upload with image resizing (using sharp), gallery upload with multiple files, and PDF upload with page count validation. Integrate with S3 for storage.

FAQ

What is the maximum file size I should allow?

5MB for images, 10MB for PDFs, 50MB for videos. Adjust based on your use case. Set limits in Multer configuration.

How do I handle image resizing on upload?

Use the sharp library after Multer processes the file. Read the file from disk or buffer, resize with sharp, and save the result.

Should I validate file types by extension or MIME type?

Validate both. MIME type is more reliable but can be spoofed. Check both the MIME type from Multer and the file extension.

How do I handle upload progress?

Multer does not support progress events out of the box. Use XMLHttpRequest upload events on the client or a library like Dropzone.

What is the best way to organize uploaded files?

Use a directory structure based on resource type and date: uploads/avatars/2026/06/. This prevents too many files in a single directory.

Mini Project

Build a complete file upload system for a user profile API. Implement avatar upload (single, image only, resized to 200x200), gallery upload (multiple images, max 5), and document upload (PDF only, max 10MB). Store files locally for development and add S3 integration for production. Include validation, error handling, and cleanup.

What's Next

Now learn about rate limiting in Building REST APIs with Node.js.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro