# Handling File Uploads in Express with Multer

## **Why File Uploads Need Middleware**

When you submit a plain HTML form name, email, that sort of thing the browser encodes everything as `application/x-www-form-urlencoded`. It's a simple key=value string, and Express's built-in body parser handles it without complaint.

But the moment a `<input type="file">` enters the picture, the rules change. The browser switches to a completely different encoding: **multipart/form-data**. Instead of a flat string, it sends a binary stream divided into labeled sections called "parts" one for each field, one for each file. Each part has headers, a boundary marker, and its own raw content.

> *Think of multipart/form-data like a series of envelopes inside one package. Each envelope is a separate field — some contain plain text, others contain binary file data.*

Express's default body parsers don't know how to open those envelopes. They'll parse JSON, they'll parse URL-encoded text, but multipart? They leave it alone. That's why you need dedicated middleware something that can read the binary stream, separate the parts, and hand them to your route handler in a usable shape.

> **Why not use body-parser?**  
> The popular `body-parser` package explicitly does not support multipart bodies. Its README says so. This is by design multipart parsing is complex enough to warrant its own focused library.

## **What Multer Is**

**Multer** is a Node.js middleware built specifically for handling `multipart/form-data`. It sits between the incoming request and your route handler, reads the binary stream, separates files from text fields, and attaches everything neatly to `req.file` or `req.files`.

Multer is built on top of **busboy**, a fast streaming multipart parser. It's the most widely used file upload middleware in the Express ecosystem, and for good reason it handles the messy parts so you don't have to.

Install it alongside Express:

```shell
npm install express multer
```

Multer's job is straightforward: intercept the request before your route handler runs, extract all the file and field data, save files to wherever you've configured, and populate `req.body` (for text fields) and `req.file` / `req.files` (for uploaded files).

  
**The Upload Lifecycle**

Before writing any code, it helps to see the full picture of what happens when a file travels from a browser to your server:

![](https://cdn.hashnode.com/uploads/covers/6950d1ca85602739c40abd67/b199e68f-b3b4-474a-bdbe-bcffd49bc288.png align="center")

Three steps, every time: the browser sends the multipart stream, Multer intercepts and processes it, and your handler receives clean, ready-to-use file data.

## **Handling a Single File Upload**

Let's start with the most common case a user uploading one file. The HTML side is just a standard form with `enctype="multipart/form-data"`. Without that attribute, the file never makes it to the server.

Create a UI

```html
<form action="/upload" method="POST" enctype="multipart/form-data">
  <input type="file" name="avatar" />
  <button type="submit">Upload</button>
</form>
```

On the server, Multer's `.single(fieldName)` method returns middleware that looks for one file in the named field:

```javascript
const express = require('express');
const multer  = require('multer');

const app    = express();
const upload = multer({ dest: 'uploads/' });

// .single('avatar') → look for a field named "avatar"
app.post('/upload', upload.single('avatar'), (req, res) => {

  // req.file contains the uploaded file metadata
  // req.body contains any other text fields
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }

  res.json({
    message:      'Upload successful',
    filename:     req.file.filename,
    originalName: req.file.originalname,
    size:         req.file.size,
    mimetype:     req.file.mimetype,
  });
});

app.listen(3000);
```

After Multer runs, `req.file` is an object with everything you need to know about the uploaded file. The most useful properties: req.file

```plaintext
{
  fieldname:   'avatar',         // HTML field name
  originalname:'profile-pic.jpg',  // filename from user's machine
  encoding:    '7bit',
  mimetype:    'image/jpeg',       // detected content type
  destination: 'uploads/',         // folder where file was saved
  filename:    'a3f9c1b2d4e8...',  // generated unique name (no ext)
  path:        'uploads/a3f9c1...',// full path on disk
  size:        82034               // bytes
}
```

> When using `dest` shorthand, Multer saves files *without their original extension*. The filename is a random hash. You'll need to rename it or use `diskStorage` if you want to preserve extensions — covered in the Storage section below.

## **Handling Multiple File Uploads**

Two situations arise with multiple files. Either the user uploads several files through *one* input field, or you have multiple fields each accepting their own files. Multer has a method for each.

### **Multiple files from a single field**

Use `.array(fieldName, maxCount)`. The second argument is optional but highly recommended it caps how many files can arrive in one request:

```javascript
app.post('/upload-many', upload.array('photos', 10), (req, res) => {

  // req.files is an array of file objects
  const filenames = req.files.map(f => f.filename);
  res.json({ uploaded: filenames });

});
```

### **Files from multiple fields**

Use `.fields()` when each field has a distinct name. Pass an array of field descriptors:

```javascript
const cpUpload = upload.fields([
  { name: 'avatar',   maxCount: 1  },
  { name: 'portfolio', maxCount: 5  },
]);

app.post('/profile', cpUpload, (req, res) => {

  // req.files is now an object, keyed by field name
  const avatar    = req.files['avatar'][0];    // one file
  const portfolio = req.files['portfolio'];    // array of files

  res.json({ avatarName: avatar.filename });
});
```

## **Storage Configuration Basics**

The `dest` shorthand is fine for quick experiments, but production code usually needs more control. Multer's `diskStorage` engine gives you two configuration hooks: where to put the file, and what to name it.

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

const storage = multer.diskStorage({

  // 1. Where should the file be saved?
  destination(req, file, cb) {
    cb(null, 'uploads/');  // first arg is error (null = no error)
  },

  // 2. What should the file be called?
  filename(req, file, cb) {
    const ext      = path.extname(file.originalname);
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
    cb(null, file.fieldname + '-' + uniqueSuffix + ext);
    // Result: "avatar-1718023456789-382910483.jpg"
  },

});

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

Both `destination` and `filename` follow the same node-style callback pattern: the first argument to `cb` is an error (pass `null` to proceed), and the second is your value. This pattern lets you run async logic like checking a database before deciding where a file should land.

## **File Type Filtering**

You don't want users uploading executable files when you're expecting images. Multer lets you filter incoming files with a `fileFilter` function. If the filter rejects a file, the request continues without it — or you can throw an error:

```javascript
function imageFilter(req, file, cb) {
  const allowed = ['image/jpeg', 'image/png', 'image/webp'];

  if (allowed.includes(file.mimetype)) {
    cb(null, true);   // accept the file
  } else {
    cb(new Error('Only JPEG, PNG, and WebP images are allowed'));
  }
}

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

## **Serving Uploaded Files**

Saving files is half the job. Once they're on disk, you need users to be able to retrieve them. Express's built-in `express.static()` middleware makes this trivially easy:

```javascript
// Serve everything in the uploads/ folder under the /uploads URL path
app.use('/uploads', express.static('uploads'));

// A file saved at uploads/avatar-1718023456.jpg
// is now available at: http://localhost:3000/uploads/avatar-1718023456.jpg
```

That one line turns your upload folder into a file server. Any file sitting in `uploads/` becomes publicly accessible via its filename in the URL.

### **Key Takeaways**

1.  HTML forms must use`enctype="multipart/form-data"`— without it, no file data is sent.
    
2.  Multer interposes itself in the middleware chain, reading the stream before your handler runs.
    
3.  Use`.single()`,`.array()`, or`.fields()`depending on your form shape.
    
4.  `diskStorage`gives full control over folder and filename; use it in production.
    
5.  Always limit file size and filter by type to prevent abuse.
    
6.  `express.static()`
