Back to Blog
Lesson 44 of the Node.js: Build Your First Server & CLI course
Node.jsSeptember 1, 20264 min read

Handling File Uploads: A Guide to Multer in Node.js

Learn to handle file uploads in your Node.js API using Multer. We cover configuring storage, processing multipart/form-data, and securing your file uploads.

Node.jsExpressMulterFile UploadMiddlewareAPI

Previously in this course, we covered Asynchronous Patterns (Promises) to manage complex operations effectively. Now that we can handle non-blocking logic, we'll turn our attention to binary data. Specifically, we'll implement file uploads, a task that requires moving beyond standard JSON request bodies.

Understanding multipart/form-data

When you send a standard JSON payload, your browser sets the Content-Type header to application/json. However, JSON is text-based and inefficient for binary data like images or PDFs.

To send files, we use the multipart/form-data encoding. This format allows a single HTTP request to contain both text fields and file buffers, separated by a unique "boundary" string. Express doesn't parse this by default, which is where Multer comes in. Multer acts as a piece of middleware that intercepts these requests, processes the binary streams, and saves them to your server’s disk (or memory) before your route handler even runs.

Step 1: Installing and Configuring Multer

First, add the package to your project:

Bash
npm install multer

In your project, you'll want to define a storage configuration. This tells Multer where to save the files and how to name them. Create a new utility file or add this to your middleware configuration:

JAVASCRIPT
const multer = require(CE9178">'multer');
const path = require(CE9178">'path');

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, CE9178">'uploads/'); // Ensure this directory exists!
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + CE9178">'-' + Math.round(Math.random() * 1E9);
    cb(null, file.fieldname + CE9178">'-' + uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({ storage: storage });

Step 2: Handling the Upload Route

To handle the request, you apply the middleware to your route. If you want to accept a single file named avatar, you use upload.single('avatar').

JAVASCRIPT
const express = require(CE9178">'express');
const router = express.Router();

router.post(CE9178">'/profile/avatar', upload.single(CE9178">'avatar'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ message: CE9178">'No file uploaded' });
  }
  
  // File is saved and path is available in req.file.path
  res.json({
    message: CE9178">'File uploaded successfully',
    filePath: req.file.path
  });
});

The middleware populates req.file with metadata (original name, encoding, size, and destination path) while writing the binary data to your uploads/ folder.

Practice Exercise

Your goal is to extend our running project's user controller. Add a new POST endpoint /users/avatar that accepts a single image file.

  1. Create an uploads directory in your root folder.
  2. Implement the multer storage configuration shown above.
  3. Apply the middleware to your route.
  4. Verify the upload using Postman by selecting "form-data" in the Body tab, setting the key to avatar, and choosing "File" as the type.

Common Pitfalls

  • Missing Directory: Multer will throw an error if the destination directory does not exist. Always ensure your code creates the uploads/ folder (or check it exists on startup).
  • Security Vulnerabilities: Never trust the client-provided filename. Always generate your own unique filenames (as shown in the filename callback) to prevent file-overwriting or path-traversal attacks. For deep dives on security, see Secure File Handling: Protecting WordPress from Upload Vulnerabilities.
  • Memory Usage: If you don't provide a storage engine, Multer saves files to memory. For large files, this will crash your server. Always use diskStorage for anything other than trivial, tiny files.

FAQ

Q: Can I upload multiple files at once? A: Yes. Use upload.array('fieldname', maxCount) for multiple files with the same field name, or upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 5 }]) for different fields.

Q: Is it better to store files in the database or on the disk? A: Generally, store the file on a filesystem or an object storage service (like S3) and save only the path/URL in your database. Databases are optimized for structured data, not heavy binary blobs.

Q: How do I filter file types? A: You can provide a fileFilter function to your multer configuration to check the mimetype of the incoming file and reject anything that isn't, for example, image/jpeg or image/png.

Recap

We've successfully moved from standard JSON APIs to handling multi-part binary data. We learned that Multer is the standard middleware for processing multipart/form-data and that proper configuration (storage engines and unique filenames) is critical for both functionality and security.

Up next: We'll automate the population of our database using a script in Database Seeding.

Similar Posts