Part 4
Image & File Uploads
Multer, Validation & Image Processing
Quick Step-by-Step Guide
Backend Development Lab
Table of Contents
Table of Contents ...............................................................................................................................2
What You Will Learn ...........................................................................................................................3
STEP 1: Install Multer & Sharp ............................................................................................................4
Create Upload Directory ......................................................................................................................4
STEP 2: Basic File Upload Setup ..........................................................................................................5
Create lib/[Link]..............................................................................................................................5
Add Upload Endpoint ...........................................................................................................................5
Update [Link] .......................................................................................................................................6
STEP 3: File Validation ........................................................................................................................7
Update lib/[Link] ............................................................................................................................7
Handle Upload Errors ...........................................................................................................................8
STEP 4: Image Processing with Sharp ..................................................................................................9
Create lib/[Link]................................................................................................................9
Update Upload Route with Processing ..............................................................................................10
STEP 5: Save Avatar to User Profile ...................................................................................................11
Update Prisma Schema ......................................................................................................................11
Update Upload Route to Save Avatar ................................................................................................11
STEP 6: Serve Uploaded Files ............................................................................................................ 13
Serve Static Files ................................................................................................................................13
Add Avatar URL to User Response .....................................................................................................13
Testing with Postman/HTTPie ........................................................................................................... 14
Using HTTPie ......................................................................................................................................14
Using cURL .........................................................................................................................................14
Postman Setup ...................................................................................................................................14
Troubleshooting ............................................................................................................................... 15
Final Project Structure ...................................................................................................................... 16
Part 4: Image & File Uploads | NodeJS Backend Lab
What You Will Learn
In this lesson, you will add image upload functionality to your API. By the end, you will be able to:
• Upload images using Multer middleware
• Validate file types (images only) and size limits
• Resize images using Sharp for thumbnails
• Save file paths to the database
• Serve uploaded files statically
Prerequisites:Part 3 (Authentication) with working User model and JWT authentication.
3 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
STEP 1: Install Multer & Sharp
Install the required packages for file handling and image processing:
npm install multer sharp
npm install --save-dev @types/multer
What was installed?Multer handles multipart/form-data (file uploads). Sharp is a fast image resizing
library.
Create Upload Directory
Create a folder to store uploaded files:
mkdir uploads
mkdir uploads/avatars
mkdir uploads/thumbnails
4 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
STEP 2: Basic File Upload Setup
Create a reusable Multer configuration:
Create lib/[Link]
// File: lib/[Link]
const multer = require('multer');
const path = require('path');
// Configure storage
const storage = [Link]({
destination: (req, file, cb) => {
cb(null, 'uploads/avatars/');
},
filename: (req, file, cb) => {
// Create unique filename: [Link]
const uniqueSuffix = [Link]() + '-' + [Link]([Link]() * 1E9);
const ext = [Link]([Link]);
cb(null, `avatar-${uniqueSuffix}${ext}`);
}
});
// Create multer instance
const upload = multer({ storage });
[Link] = { upload };
Add Upload Endpoint
Create a new route for avatar uploads:
File: routes/[Link]
const express = require('express');
const router = [Link]();
const { upload } = require('../lib/upload');
const { requireAuth } = require('../middleware/auth');
// POST /api/uploads/avatar
// Single file upload with field name 'avatar'
[Link]('/avatar',
requireAuth,
5 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
[Link]('avatar'),
(req, res) => {
if (![Link]) {
return [Link](400).json({ error: 'No file uploaded' });
}
[Link]({
message: 'Avatar uploaded successfully',
filename: [Link],
path: [Link],
size: [Link]
});
}
);
[Link] = router;
Update [Link]
const uploadRoutes = require('./routes/uploads');
[Link]('/api/uploads', uploadRoutes);
6 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
STEP 3: File Validation
Add validation for file type and size:
Update lib/[Link]
// File: lib/[Link] (updated with validation)
const multer = require('multer');
const path = require('path');
// Allowed file types
const ALLOWED_TYPES = /jpeg|jpg|png|gif|webp/;
// Max file size: 5MB
const MAX_SIZE = 5 * 1024 * 1024;
const storage = [Link]({
destination: (req, file, cb) => {
cb(null, 'uploads/avatars/');
},
filename: (req, file, cb) => {
const uniqueSuffix = [Link]() + '-' + [Link]([Link]() * 1E9);
const ext = [Link]([Link]).toLowerCase();
cb(null, `avatar-${uniqueSuffix}${ext}`);
}
});
// File filter for type validation
const fileFilter = (req, file, cb) => {
const extname = ALLOWED_TYPES.test(
[Link]([Link]).toLowerCase()
);
const mimetype = ALLOWED_TYPES.test([Link]);
if (extname && mimetype) {
return cb(null, true);
} else {
cb(new Error('Only image files (jpeg, jpg, png, gif, webp) are allowed'));
}
};
const upload = multer({
7 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
storage,
fileFilter,
limits: { fileSize: MAX_SIZE }
});
[Link] = { upload };
Handle Upload Errors
// Update routes/[Link] with error handling
const handleUploadError = (err, req, res, next) => {
if (err instanceof [Link]) {
// Multer-specific errors
if ([Link] === 'LIMIT_FILE_SIZE') {
return [Link](400).json({
error: 'File too large. Max size is 5MB.'
});
}
return [Link](400).json({ error: [Link] });
}
if (err) {
return [Link](400).json({ error: [Link] });
}
next();
};
[Link]('/avatar',
requireAuth,
[Link]('avatar'),
handleUploadError,
(req, res) => {
// ... response
}
);
8 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
STEP 4: Image Processing with Sharp
Automatically resize uploaded images to create thumbnails:
Create lib/[Link]
// File: lib/[Link]
const sharp = require('sharp');
const path = require('path');
async function createThumbnail(inputPath, width = 200, height = 200) {
const filename = [Link](inputPath, [Link](inputPath));
const outputPath = `uploads/thumbnails/${filename}-[Link]`;
await sharp(inputPath)
.resize(width, height, {
fit: 'cover',
position: 'center'
})
.jpeg({ quality: 80 })
.toFile(outputPath);
return outputPath;
}
async function resizeImage(inputPath, maxWidth = 1200) {
const filename = [Link](inputPath);
const outputPath = `uploads/avatars/resized-${filename}`;
await sharp(inputPath)
.resize(maxWidth, null, {
withoutEnlargement: true
})
.jpeg({ quality: 85 })
.toFile(outputPath);
return outputPath;
}
[Link] = { createThumbnail, resizeImage };
9 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
Update Upload Route with Processing
// File: routes/[Link] (updated)
const { createThumbnail, resizeImage } = require('../lib/imageProcessor');
[Link]('/avatar',
requireAuth,
[Link]('avatar'),
handleUploadError,
async (req, res) => {
try {
if (![Link]) {
return [Link](400).json({ error: 'No file uploaded' });
}
// Process images
const thumbnailPath = await createThumbnail([Link], 150, 150);
const resizedPath = await resizeImage([Link], 800);
[Link]({
message: 'Avatar uploaded and processed',
original: [Link],
thumbnail: [Link](thumbnailPath),
resized: [Link](resizedPath),
size: [Link]
});
} catch (error) {
[Link]('Image processing error:', error);
[Link](500).json({ error: 'Image processing failed' });
}
}
);
10 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
STEP 5: Save Avatar to User Profile
Update the User model and save avatar URL to database:
Update Prisma Schema
// Add to prisma/[Link]
model User {
id Int @id @default(autoincrement())
email String @unique
name String
role String @default('student')
passwordHash String
avatar String? // NEW: Avatar filename (optional)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map('users')
}
// Run migration
npx prisma migrate dev --name add_user_avatar
Update Upload Route to Save Avatar
// File: routes/[Link]
const prisma = require('../lib/prisma');
[Link]('/avatar',
requireAuth,
[Link]('avatar'),
handleUploadError,
async (req, res) => {
try {
if (![Link]) {
return [Link](400).json({ error: 'No file uploaded' });
}
// Process image
const thumbnailPath = await createThumbnail([Link], 150, 150);
// Update user avatar in database
11 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
const user = await [Link]({
where: { id: [Link] },
data: { avatar: [Link] },
select: { id: true, name: true, email: true, avatar: true }
});
[Link]({
message: 'Avatar updated successfully',
user
});
} catch (error) {
[Link]('Upload error:', error);
[Link](500).json({ error: 'Upload failed' });
}
}
);
12 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
STEP 6: Serve Uploaded Files
Make uploaded files accessible via URL:
Serve Static Files
// File: [Link]
const path = require('path');
// Serve uploads folder statically
[Link]('/uploads', [Link]([Link](__dirname, 'uploads')));
// Now files are accessible at:
// [Link]
Add Avatar URL to User Response
// File: routes/[Link]
const BASE_URL = [Link].BASE_URL || '[Link]
// Helper to add avatar URL
function addAvatarUrl(user) {
return {
...user,
avatarUrl: [Link] ? `${BASE_URL}/uploads/avatars/${[Link]}` : null
};
}
// In GET /api/users/:id
[Link]('/:id', requireAuth, async (req, res) => {
const user = await [Link]({
where: { id: parseInt([Link]) },
select: { id: true, name: true, email: true, avatar: true, role: true }
});
if (!user) {
return [Link](404).json({ error: 'User not found' });
}
[Link](addAvatarUrl(user));
});
13 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
Testing with Postman/HTTPie
Using HTTPie
# Upload avatar (requires auth token)
http -f POST :3000/api/uploads/avatar \
Authorization:'Bearer <your_token>' \
avatar@~/Pictures/[Link]
Using cURL
curl -X POST [Link] \
-H 'Authorization: Bearer <your_token>' \
-F 'avatar=@/path/to/[Link]'
Postman Setup
1. Set method to POST
2. URL: [Link]
3. Headers: Authorization: Bearer <token>
4. Body: Select 'form-data'
5. Key: avatar (type: File)
6. Value: Select your image file
14 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
Troubleshooting
Error: 'Cannot find module multer'
Fix:Multer not installed. Run: npm install multer
Error: 'Unexpected field'
Fix:The field name in form doesn't match. Ensure field name is 'avatar' in your form/Postman
Error: 'File too large'
Fix:File exceeds 5MB limit. Resize image or increase MAX_SIZE in [Link]
Images not accessible via URL
Fix:Check that [Link]('/uploads', [Link](...)) is before your routes
Sharp installation fails
Fix:Sharp requires native dependencies. On Mac: brew install vips. On Ubuntu: sudo apt install
libvips-dev
15 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
Final Project Structure
backend-lab/
├── uploads/
│ ├── avatars/ # Original uploaded avatars
│ └── thumbnails/ # Generated thumbnails
├── lib/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link] # NEW: Multer config
│ └── [Link] # NEW: Sharp image processing
├── routes/
│ ├── [Link]
│ ├── [Link]
│ └── [Link] # NEW: Upload routes
├── middleware/
│ └── [Link]
├── prisma/
│ └── [Link] # Updated with avatar field
├── [Link] # Added static file serving
└── [Link]
Success!Your API now supports image uploads with validation and processing.
Next Steps:Cloud storage (AWS S3), multiple file uploads, or delete old avatars on update.
16 / 17
Part 4: Image & File Uploads | NodeJS Backend Lab
Backend Development Lab
Part 4: Image & File Uploads
Quick Step-by-Step Guide
17 / 17