In this post, you'll learn how to build a clean, reusable backend API in Node.js + TypeScript using Express and Multer to handle file uploads. We'll keep it simple — no file type restrictions or size limits — just basic upload functionality that saves files using their original name.
🛠 Tech Stack
- Express: Web framework for Node.js
- 
Multer: Middleware for handling multipart/form-data(file uploads)
- TypeScript: Optional static typing
- MVC Structure: For clean separation of logic
🗂 Folder Structure
file-upload-api/
├── controllers/
│   └── upload.controller.ts
├── middleware/
│   └── upload.middleware.ts
├── routes/
│   └── upload.route.ts
├── services/
│   └── upload.service.ts
├── uploads/  (created automatically)
├── index.ts
├── tsconfig.json
└── package.json
📦 Step 1: Install Dependencies
npm install express multer
npm install -D typescript ts-node-dev @types/node @types/express @types/multer
Add a simple tsconfig.json if you don’t have one yet:
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["./**/*.ts"]
}
✍️ Step 2: Multer Middleware
middleware/upload.middleware.ts
import multer from 'multer';
import path from 'path';
import fs from 'fs';
// Ensure uploads directory exists
const uploadDir = path.join(process.cwd(), 'uploads');
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}
const storage = multer.diskStorage({
  destination: (_req, _file, cb) => cb(null, uploadDir),
  filename: (_req, file, cb) => {
    const timestamp = Date.now();
    cb(null, `${timestamp}-${file.originalname}`);
  },
});
export const upload = multer({ storage });
💼 Step 3: Upload Service
services/upload.service.ts
export const handleUploadService = (file: Express.Multer.File) => {
  return {
    message: 'File uploaded successfully!',
    originalName: file.originalname,
    filename: file.filename,
    path: file.path,
    size: file.size,
  };
};
📤 Step 4: Upload Controller
controllers/upload.controller.ts
import { Request, Response } from 'express';
import { handleUploadService } from '../services/upload.service';
export const uploadFile = (req: Request, res: Response) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }
  const data = handleUploadService(req.file);
  res.status(200).json(data);
};
🛣 Step 5: Define the Route
routes/upload.route.ts
import { Router } from 'express';
import { upload } from '../middleware/upload.middleware';
import { uploadFile } from '../controllers/upload.controller';
const router = Router();
router.post('/file', upload.single('file'), uploadFile);
export default router;
🚀 Step 6: Initialize Express Server
index.ts
import express from 'express';
import uploadRoute from './routes/upload.route';
const app = express();
const PORT = 3000;
app.use(express.json());
app.use('/api/upload', uploadRoute);
app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});
🧪 Test It with Postman
- 
Method: POST
- 
URL: http://localhost:3000/api/upload/file
- 
Body: form-data- Key: file(type:File)
- Select a file from your system
 
- Key: 
You should get a JSON response like:
{
  "message": "File uploaded successfully!",
  "originalName": "video.mp4",
  "filename": "1713967431914-video.mp4",
  "path": "/uploads/1713967431914-video.mp4",
  "size": 123456
}
✅ Conclusion
You now have a robust, scalable backend setup for handling video uploads using Express, TypeScript and Multer. It saves with original file names (timestamp-prefixed), and follows clean code separation via MVC.
 

 
    
Top comments (0)