Handling File Uploads in Express with Multer

File uploads are one of the most common features in backend applications. Whether users are uploading profile images, PDFs, resumes, or product photos, the server needs a proper way to receive and store those files.
In Express applications, handling uploads is not as straightforward as handling normal JSON data. Files are sent differently by the browser, which is why we need specialized middleware like Multer.
In this article, we’ll understand how file uploads work in Express using Multer, how to upload single and multiple files, and how uploaded files are eventually served to users.
Why file uploads need middleware
When we send normal form data, Express can easily read it using middleware like:
app.use(express.json());
But file uploads work differently.
Files are usually sent using a format called:
multipart/form-data
This format contains:
file data
form fields
metadata
Unlike JSON, Express cannot parse multipart data by default.
That’s why file upload middleware is required.
Without middleware:
req.bodymay work for text fieldsuploaded files will not be processed correctly
This is where Multer helps.
What Multer is ?
Multer is a middleware for Express that handles multipart/form-data.
Its main job is to:
receive uploaded files
process them
store them on the server
provide file information inside the request object
Install Multer:
npm install multer
Basic Setup :
import express from "express";
import multer from "multer";
const app = express();
const upload = multer({
dest: "uploads/"
});
Here :
Multer stores uploaded files inside the
uploadsfolderfile information becomes available in the request
Understanding the upload lifecycle
Here’s what happens behind the scenes during a file upload:
Client Selects File
↓
Browser Sends multipart/form-data Request
↓
Multer Middleware Receives File
↓
Multer Processes File
↓
File Stored in uploads Folder
↓
Request Continues to Route Handler
Multer acts like a middle layer between the incoming request and your route handler.
Handling single file upload
Single file uploads are commonly used for:
profile pictures
thumbnails
resumes
documents
Example:
import express from "express";
import multer from "multer";
const app = express();
const upload = multer({
dest: "uploads/"
});
app.post("/upload", upload.single("image"), (req, res) => {
res.json({
message: "File uploaded successfully",
file: req.file
});
});
app.listen(3000);
Important part:
upload.single("image")
This means:
only one file is expected
the input field name must be
image
Example frontend form:
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="image" />
<button type="submit">
Upload
</button>
</form>
The enctype="multipart/form-data" is very important. Without it, files will not be sent properly.
After upload:
file details are available inside
req.fileMulter automatically stores the file
Handling multiple file uploads
Sometimes users need to upload multiple files together.
Examples:
product gallery images
project documents
media uploads
Multer provides:
upload.array()
Example:
import express from "express";
import multer from "multer";
const app = express();
const upload = multer({
dest: "uploads/"
});
app.post("/uploads", upload.array("images", 5), (req, res) => {
res.json({
message: "Files uploaded successfully",
files: req.files
});
});
Here:
upload.array("images", 5)
means:
field name is
imagesmaximum 5 files allowed
Frontend example:
<form action="/uploads" method="POST" enctype="multipart/form-data">
<input type="file" name="images" multiple />
<button type="submit">
Upload Files
</button>
</form>
Uploaded file details are available in:
req.files
Storage configuration basics
By default, Multer generates random file names.
But in real applications, developers usually configure storage manually.
Example:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename: (req, file, cb) => {
const uniqueName =
Date.now() + "-" + file.originalname;
cb(null, uniqueName);
}
});
const upload = multer({ storage });
This configuration helps:
organize uploaded files
avoid duplicate file names
generate readable file names
Example generated file:
174689223-profile.png
Minimal folder structure:
project/
│
├── uploads/
├── server.js
└── package.json
Keeping the structure simple makes uploads easier to understand for beginners.
Serving uploaded files
Uploading files is only part of the process. Users also need access to those uploaded files.
Express provides static middleware for this.
Example:
app.use("/uploads", express.static("uploads"));
This line tells Express:
“Everything inside the uploads folder can be accessed publicly.”
Suppose the folder contains:
uploads/
└── profile.png
The file becomes accessible at:
http://localhost:3000/uploads/profile.png
This URL can be used:
inside
<img>tagsin frontend applications
for downloads
in mobile apps
Complete example:
import express from "express";
import multer from "multer";
const app = express();
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename: (req, file, cb) => {
const uniqueName =
Date.now() + "-" + file.originalname;
cb(null, uniqueName);
}
});
const upload = multer({ storage });
app.use("/uploads", express.static("uploads"));
app.post("/upload", upload.single("image"), (req, res) => {
const fileUrl =
`\({req.protocol}://\){req.get("host")}/uploads/${req.file.filename}`;
res.json({
message: "Upload successful",
url: fileUrl
});
});
app.listen(3000);
Multer middleware execution flow
Here’s the complete execution flow:
User Selects File
↓
Browser Sends multipart/form-data Request
↓
Multer Middleware Executes
↓
File Saved in uploads Folder
↓
req.file or req.files Created
↓
Route Handler Executes
↓
Response Sent to Client
Understanding this flow makes it much easier to debug upload-related issues in Express applications.
Conclusion
File uploads may look simple from the frontend, but a lot happens behind the scenes on the server. Since Express cannot process multipart file data on its own, middleware like Multer becomes essential for handling uploads properly.
Once you understand how Multer receives files, stores them, and passes file information to route handlers, building features like profile image uploads, document systems, or media galleries becomes much easier. Starting with local storage is the best way to learn the complete upload flow before moving toward advanced solutions like cloud storage or CDN-based systems later.




