Handling File Uploads in Express with Multer

The first time I tried handling file uploads in Express…
I thought it would work like normal form data.
Send request.
Read req.body.
Done.
Simple.
But then I tried uploading a file…
and nothing made sense.
The file wasn’t where I expected.
req.body didn’t behave the way I thought.
And I remember thinking…
Why is uploading a file different from sending normal text fields?
That was my first encounter with why file uploads need special handling.
And honestly…
that confusion led me to Multer.
Why File Uploads Need Middleware
This clicked when I realized:
A file is not like a regular text value.
Sending:
name=Sahil
is one thing.
Sending an image…
very different.
There is more data.
Different structure.
Different processing.
And Express doesn’t magically handle all of that by itself.
That’s where middleware comes in.
The Word That Confused Me
multipart/form-data
This sounded scary the first time I saw it.
It really didn’t need to.
Simple beginner understanding:
It’s a format browsers use when sending files.
That’s enough.
I didn’t need deeper protocol details to understand uploads.
And honestly…
that was enough to move forward.
I Think Of It Like Sending A Package
This analogy helped me.
Normal form field:
Like sending a note.
Very simple.
File upload:
Like sending a package.
Needs different handling.
That made it click.
Client → Server → Storage Flow
What Multer Is
Simple version:
Multer is Express middleware for handling file uploads.
That’s it.
It sits in the request flow…
helps process incoming files…
and makes them available to your code.
That’s how I understood it.
And that was enough.
First Time I Used It
Install:
npm install multer
Then:
const multer =
require("multer");
That was my starting point.
Single File Upload
This was the first thing that made sense.
const upload = multer({
dest:"uploads/"
});
Then:
app.post(
"/upload",
upload.single(
"photo"
),
(req,res)=>{
res.send(
"Uploaded"
);
}
);
And I remember thinking…
wait…
that’s cleaner than I expected.
What single("photo") Means
This confused me initially.
It refers to the field name.
Like:
<input
type="file"
name="photo"
/>
That name:
photo
matches:
upload.single("photo")
Once I saw that…
it clicked.
Multer Middleware Flow
Accessing Uploaded File
This was cool.
Multer puts file info on:
req.file
Example:
console.log(
req.file
);
And suddenly I could actually see uploaded file data.
That felt satisfying.
Multiple File Uploads
Then I learned:
It isn’t just single files.
You can do multiple too.
app.post(
"/photos",
upload.array(
"images",
3
),
(req,res)=>{
res.send(
"Files uploaded"
);
}
);
Now:
req.files
instead of:
req.file
That was a nice distinction.
That Was My “Ohhh” Moment
Single file:
req.file
Multiple:
req.files
Simple.
But important.
That helped me remember it.
Storage Configuration Basics
At first I used:
dest:"uploads/"
Very simple.
And honestly…
good enough to start.
Later I learned you can customize storage.
Like filename control.
Destination control.
Example:
const storage =
multer.diskStorage({
destination:
function(
req,file,cb
){
cb(
null,
"uploads/"
);
},
filename:
function(
req,file,cb
){
cb(
null,
file.originalname
);
}
});
Then:
const upload=
multer({
storage
});
That looked intimidating at first.
But conceptually…
it is just saying:
Where to save.
How to name.
That’s all.
Serving Uploaded Files
This part was new to me.
Uploading is one thing.
Serving uploaded files back is another.
Simple Express setup:
app.use(
"/uploads",
express.static(
"uploads"
)
);
That makes uploaded files accessible.
And when I first saw that…
it connected the whole flow for me.
Upload.
Store.
Serve.
Complete cycle.
Upload Lifecycle In My Head
User selects file.
Browser sends file.
Multer processes it.
Server stores it.
App can access it.
That mental sequence helped me.
A lot.
One Mistake I Made
I thought file would appear in:
req.body
Wrong.
That confused me early.
File data comes through Multer handling.
Different path.
Important distinction.
Another Thing I Got Wrong
I assumed Multer stores in cloud.
No.
Multer handles upload processing.
Storage decisions are separate.
I mixed those together initially.
Worth saying.
Tiny Practice Example
Start with one file:
upload.single(
"avatar"
)
Then try:
console.log(
req.file
);
Seeing that object teaches a lot.
Honestly.
Why Middleware Made More Sense Here
Before this…
middleware felt abstract.
With Multer…
I saw middleware doing real work.
Request comes in.
Middleware processes file.
Route gets clean access.
That made middleware click even more.
What Finally Made It Click
I stopped thinking:
File upload is just another form field.
And started thinking:
It needs its own processing pipeline.
That made everything make sense.
And that is why Multer exists.
Quick Recap
File uploads need middleware
Multer handles upload processing
single()for one filearray()for multiple filesStorage can be configured
Uploaded files can be served back
That’s the foundation.
Conclusion
The first time I tried file uploads…
I expected it to work like normal form data.
It didn’t.
And that confusion is what led me to understand Multer.
Now it feels much simpler.
Request comes in.
Multer processes file.
Server stores it.
Route handles result.
That’s the core.
If you remember one thing from this article, remember this:
File uploads need special request processing…
and that is exactly where Multer helps.
That idea made it click for me.
If you like this simple learning-style explanation,
I write more notes at
devwithsahil.hashnode.dev
and share progress on LinkedIn 🙂



