Skip to main content

Command Palette

Search for a command to run...

Storing Uploaded Files and Serving Them in Express

Understanding file storage, static serving, and upload handling in Express.js.

Published
•6 min read
Storing Uploaded Files and Serving Them in Express
G
I enjoy blending technology with business to build solutions that create real impact. I’m fascinated by how technology empowers people, businesses, and startups. I believe thoughtful use of technology quietly improves lives, and that belief fuels everything I build and explore 💻.

If you’ve recently learned how file uploads work in Express using Multer, the next important thing to understand is what actually happens after the upload completes. A lot of beginners initially focus only on receiving the file from the client, but real backend applications also need proper storage handling and a way to serve those uploaded files back to users.

For example, when someone uploads a profile picture, the backend not only stores the image somewhere on the server but also later provides a URL so the frontend can display that image again. The same thing happens with resumes, PDFs, product images, documents, and media uploads in real-world applications.

In this blog, we’ll understand where uploaded files are stored, the difference between local and external storage at a high level, how static file serving works in Express.js, how uploaded files become accessible through URLs, and some important security considerations developers should keep in mind while handling uploads.

Where Uploaded Files Are Stored

Whenever a file gets uploaded using Multer, the backend needs a destination where that file will physically exist after upload completion.

Usually, developers create a folder like:

/uploads

Inside the project directory.

Example structure:

project-folder
│
├── uploads
│   ├── image1.png
│   ├── resume.pdf
│
├── server.js

This uploads folder becomes responsible for storing all uploaded files locally on the server machine.

Multer usually handles this using storage configuration.

Example:

const multer = require("multer");

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "uploads/");
  },

  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + file.originalname);
  }
});

Here:

  • destination defines storage location

  • filename defines stored file naming behavior

Understanding Local Storage

Local storage simply means files are stored directly inside the backend server’s filesystem.

For example:

/uploads/profile.png

This is the simplest storage approach and is very commonly used while learning backend development or building smaller applications.

Local storage works well because:

  • Setup is simple

  • Files remain directly accessible

  • No external services are needed

  • Easy for development environments

But local storage also has limitations in large-scale production systems because files remain tied to one particular server machine.

Local Storage vs External Storage

In real production applications, many companies eventually move toward external storage services instead of storing files directly on backend servers.

Examples include:

  • AWS S3

  • Cloudinary

  • Firebase Storage

The reason is scalability.

Suppose a server crashes or multiple backend servers exist together. Local storage becomes harder to manage consistently across systems. External storage services solve this by managing file availability separately.

But initially, local storage is more than enough for understanding backend upload systems properly, which is why most beginner projects start with folder-based local storage itself.

Understanding Static File Serving

One important thing beginners often miss is that storing files is not enough. The frontend also needs a way to access those uploaded files later.

This is where static file serving comes into the picture.

Express provides built-in middleware for serving static files publicly.

Example:

app.use("/uploads", express.static("uploads"));

This single line exposes the uploads folder publicly through URLs.

Here:

  • First "uploads" defines the URL path

  • Second "uploads" defines the actual folder location

Now Express automatically serves files from that folder whenever requests arrive.

Accessing Uploaded Files Through URLs

Once static serving is configured, uploaded files become accessible using URLs.

Suppose the stored file is:

/uploads/photo.png

The frontend can access it using:

http://localhost:3000/uploads/photo.png

This is exactly how profile images, uploaded documents, and media files are later displayed in frontend applications.

Usually, backend applications store these file paths inside databases after successful uploads so they can later retrieve and display uploaded content easily.

Complete Upload and Serving Flow

The overall upload flow usually looks like this:

  • Client uploads file

  • Multer processes request

  • File gets stored inside uploads folder

  • Backend stores file path

  • Express serves file statically

  • Frontend accesses file through URL

This entire cycle becomes one of the most common backend workflows in applications involving media handling.

Example Upload Route

Basic upload route:

app.post("/upload", upload.single("image"), (req, res) => {
  res.send(req.file.path);
});

Possible response:

uploads/174687-photo.png

This returned path can later be used for displaying uploaded files on the frontend.

Security Considerations for File Uploads

One very important thing while handling uploads is security.

Allowing users to upload files without restrictions can become dangerous because malicious files may get uploaded to the server.

Some important safety practices include:

  • Restricting allowed file types

  • Limiting file size

  • Renaming uploaded files

  • Avoiding direct execution permissions

  • Validating upload inputs properly

For example, many applications only allow:

  • JPG

  • PNG

  • PDF

instead of accepting every possible file type.

Limiting File Types

Multer supports file filtering.

Example:

const upload = multer({
  storage,

  fileFilter: (req, file, cb) => {
    if (file.mimetype === "image/png") {
      cb(null, true);
    } else {
      cb(null, false);
    }
  }
});

This prevents unwanted file formats from getting uploaded.

File Size Limits

Large uploads can also become problematic because they consume server storage and bandwidth.

Example:

const upload = multer({
  storage,

  limits: {
    fileSize: 1024 * 1024
  }
});

Here:

  • Maximum upload size becomes 1MB

This helps protect backend systems from extremely large uploads.

Why Static Serving Matters

Without static serving, uploaded files would remain physically stored on the server but inaccessible to clients.

Static middleware acts as the bridge between stored files and public access URLs. This is why Express static middleware becomes extremely important for image galleries, profile pictures, product images, document downloads, and media-based applications.

Final Thoughts

Handling uploads in Express.js is not only about receiving files from the client. Proper backend systems also need organized storage handling, public access mechanisms, and secure upload validation.

Local storage provides a simple and beginner-friendly way of understanding upload workflows, while Express static middleware makes uploaded files accessible through URLs. Combined with Multer, this creates a complete upload pipeline capable of supporting profile images, PDFs, product galleries, and many other real-world backend features.