Skip to main content
Madhukar
All Articles

Handling File Uploads in Express with Multer

September 7, 20265 min read
web-developmentexpressjsbackend-developmentnodejsmulter
Handling File Uploads in Express with Multer

Why File Uploads Need Middleware

A normal form submission sends simple text data, which Express can parse easily with its built-in express.json() or express.urlencoded(). A file upload is fundamentally different — the browser sends the request using a special encoding called multipart/form-data, which mixes regular form fields and raw binary file data together in one request body.

Express’s built-in parsers don’t know how to handle this format at all — which is exactly the gap Multer exists to fill.

What Multer Is

Multer is a middleware for Express, built specifically to parse multipart/form-data requests — extracting uploaded files, saving them somewhere (like disk), and attaching information about them directly onto the request object, ready for your route handler to use.

npm install multer
const multer = require("multer");
const upload = multer({ dest: "uploads/" });

multer({ dest: "uploads/" }) creates a configured Multer instance that saves uploaded files directly into an uploads/ folder — the simplest possible setup, and a solid starting point before adding more specific configuration.

Handling Single File Upload

To handle a route that accepts exactly one uploaded file, use upload.single(fieldName) as middleware, where fieldName matches the form field's name attribute on the client side.

const express = require("express");
const multer = require("multer");
const app = express();

const upload = multer({ dest: "uploads/" });

app.post("/upload", upload.single("avatar"), (req, res) => {
console.log(req.file); // details about the uploaded file
res.send(`File uploaded: ${req.file.filename}`);
});

Corresponding HTML form:

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

req.file (singular) is populated automatically by Multer, containing details like the original filename, the saved filename, its size, and its path on disk.

Handling Multiple File Uploads

For multiple files under the same field name, use upload.array(fieldName, maxCount) instead:

app.post("/upload-multiple", upload.array("photos", 5), (req, res) => {
console.log(req.files); // an array of uploaded file details
res.send(`${req.files.length} files uploaded`);
});
<form action="/upload-multiple" method="POST" enctype="multipart/form-data">
<input type="file" name="photos" multiple />
<button type="submit">Upload</button>
</form>

Here, req.files (plural) is an array, with one entry per uploaded file — maxCount (5, in this example) caps how many files Multer will accept in a single request.

Multiple fields, each with their own file(s)

const uploadFields = multer({ dest: "uploads/" }).fields([
{ name: "avatar", maxCount: 1 },
{ name: "gallery", maxCount: 5 },
]);

app.post("/profile", uploadFields, (req, res) => {
console.log(req.files.avatar); // array with one file
console.log(req.files.gallery); // array with up to five files
res.send("Profile updated");
});

.fields() handles the situation where a single form has multiple, differently-named file inputs — each tracked separately under its own key in req.files.

Storage Configuration Basics

The simple dest: "uploads/" option saves files with Multer's auto-generated names. For more control, use multer.diskStorage():

const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/"); // where to save the file
},
filename: (req, file, cb) => {
const uniqueName = `${Date.now()}-${file.originalname}`;
cb(null, uniqueName); // what to name the saved file
},
});

const upload = multer({ storage });
  • destination — decides which folder the file is saved into
  • filename — decides what the saved file is actually named (here, prefixing the original name with a timestamp, to avoid naming collisions)

This minimal disk-based setup is intentionally kept simple here — production systems often move to cloud storage (like Amazon S3) instead, a separate, more advanced configuration worth exploring once this basic pattern feels comfortable.

Serving Uploaded Files

Once files are saved, Express’s built-in static file serving (covered in this series’s dedicated article on storing and serving uploads) makes them accessible directly by URL:

app.use("/uploads", express.static("uploads"));
<img src="http://localhost:3000/uploads/1699999999-avatar.png" alt="User avatar" />

Any file saved into the uploads/ folder automatically becomes reachable at a matching URL — no separate route needed just to serve the raw file back out.

Final Takeaway

File uploads need their own dedicated middleware because multipart/form-data is a fundamentally different request format from the plain JSON or form data Express already knows how to parse. Multer fills that gap cleanly — upload.single() for one file, upload.array() or .fields() for several, and diskStorage() when you need control over exactly where and how files get saved. Once files are safely stored, Express's own static file serving makes them reachable by URL, completing the full journey from a user's device, through Multer, onto disk, and back out to anyone who needs to view it.

Frequently Asked Questions

Does the field name in upload.single() need to match anything specific?

> Yes — it must exactly match the name attribute of the file input in your HTML form (or however the request is constructed). A mismatch here is one of the most common early Multer bugs — req.file will simply be undefined if the names don't align.

What happens to req.file if no file was actually uploaded?

> It will be undefined — it's good practice to check for this explicitly in your route handler before trying to access properties on it, to avoid a runtime error on requests that didn't include a file.

Should I validate file type and size with Multer?

> Yes — Multer supports a fileFilter option and limits configuration (like maximum file size) directly in its setup, letting you reject invalid uploads early, before they're even saved to disk. This ties directly into the security considerations covered in this series's broader article on storing and serving uploaded files.

Is disk storage the right choice for a production application?

> It’s a reasonable starting point for learning and smaller projects, but production systems — especially ones deployed across multiple servers or ephemeral hosting environments — typically move to dedicated cloud storage instead, which Multer also supports through additional storage engine configurations beyond the basics covered here.

Handling File Uploads in Express with Multer was originally published in Towards Dev on Medium, where people are continuing the conversation by highlighting and responding to this story.

Originally published by Mr Madhukar

Read the complete article on Medium with full formatting & reader responses.