Stop 05 · Express.js & Web FrameworksProjects · 3/3

File Upload Service

Part of the Backend Developer Roadmap

In short

A file upload service is a small backend that only does one job: accept files safely, store them, and give them back. It's a perfect project to learn limits, streams and security.

A dedicated service for accepting, validating and storing file uploads — practice for Multer, file validation, and cloud storage integration.

The mascot hands a suitcase over a cloakroom counter and gets a claim token.

Think of it like this

The railway cloakroom

At the station cloakroom, you hand over your bag, it's checked and shelved, and you get a token. Later, show the token and get your bag back. Your service does the same: take the file, store it, return an id to fetch it later.

Watch it happen

Upload, then download

Client
Upload API
Storage
Metadata DB

POST /files (multipart)

1/6The client uploads invoice.pdf in a field called file.

The big ideas

What you need to know

Limits first

Max size, allowed types and maybe max files per user per day. Decide these before writing any code.

Metadata separate

Store the file in storage and its details in a database. Listing files never touches the disk.

Private by default

Downloads check who's asking. Never serve the uploads folder with express.static.

Stream downloads

res.download or a read stream sends big files in chunks, so memory stays low.

See it in code

Walk through the code

js
1app.get("/files/:id", requireAuth, async (req, res, next) => {2  const meta = await db.files.findById(req.params.id);3  if (!meta || meta.ownerId !== req.user.id) {4    return res.status(404).json({ error: "File not found" });5  }6 7  const filePath = path.join(UPLOAD_DIR, meta.storedName);8  res.download(filePath, meta.originalName, (err) => {9    if (err && !res.headersSent) next(err);10  });11});

1/4Only logged-in users can download. Look up the file's metadata by id.

Returning 404 instead of 403 for someone else's file means nobody can tell whether a guessed id exists.

Save yourself the pain

Common mistakes

Oops

Building the file path from req.params directly, like path.join(dir, req.params.name).

Do this instead

Look up the stored name in your database. User input in paths allows ../ tricks to read other files.

Oops

Reading the whole file into memory with fs.readFile before sending it.

Do this instead

Stream it with res.download or fs.createReadStream so large files don't eat your RAM.

Your mission

Milestone 1: upload and fetch

~45 min

Quick check

Why store an uploaded file under a random name instead of its original filename?

Go deeper

  1. 1Accept uploads with Multer and validate file type/size before saving.
  2. 2Upload files to cloud storage (S3 or equivalent) instead of local disk.
  3. 3Return a public or signed URL for each uploaded file.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge