16 File Upload
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
- 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.
- Allowing unlimited file sizes — Large files can exhaust disk space and memory. Set reasonable size limits based on your use case.
- Saving files with user-provided names — Original filenames may contain path traversal characters (../). Generate unique filenames server-side.
- Not cleaning up failed uploads — If upload starts but fails validation mid-stream, the partial file wastes space. Use temporary directories and cleanup routines.
- 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
- What middleware handles file uploads in Express?
- How do you validate file types with Multer?
- What is the difference between single, array, and fields upload methods?
- Why should you use cloud storage instead of local storage in production?
- 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
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