Documentation Index Fetch the complete documentation index at: https://mintlify.com/memoowi/e-comm-api-demo-2/llms.txt
Use this file to discover all available pages before exploring further.
The E-Commerce API supports file uploads for product images and user profile photos using Multer. This guide explains how to configure and use file uploads.
Overview
File uploads are handled by the multer middleware, which:
Stores files on disk in organized directories
Validates file types (images only)
Enforces file size limits (5MB maximum)
Generates unique filenames to prevent conflicts
Upload configuration
The upload middleware is configured in middlewares/fileUpload.mjs:
middlewares/fileUpload.mjs
import multer from 'multer' ;
import path from 'path' ;
import fs from 'fs' ;
const createDirectory = ( dir ) => {
if ( ! fs . existsSync ( dir )) {
fs . mkdirSync ( dir , { recursive: true });
}
};
const storage = multer . diskStorage ({
destination : ( req , file , cb ) => {
const currentUrl = req . originalUrl ;
const route = currentUrl . split ( '/' )[ 3 ];
const uploadPath = path . join ( 'uploads' , route );
createDirectory ( uploadPath );
cb ( null , uploadPath );
},
filename : ( req , file , cb ) => {
const uniqueSuffix = Date . now () + '-' + Math . round ( Math . random () * 1e9 );
cb ( null , uniqueSuffix + path . extname ( file . originalname ));
},
});
const fileFilter = ( req , file , cb ) => {
const allowedTypes = [ 'image/jpeg' , 'image/png' , 'image/gif' , 'image/jpg' , 'image/bmp' , 'image/webp' ];
if ( allowedTypes . includes ( file . mimetype )) {
cb ( null , true );
} else {
cb ( new Error ( 'Invalid file type. Only JPEG, PNG, GIF, JPG, BMP, and WEBP files are allowed.' ), false );
}
};
export const upload = multer ({
storage ,
fileFilter ,
limits: { fileSize: 1024 * 1024 * 5 }, // 5MB limit
});
Supported file types
The API accepts the following image formats:
JPEG (.jpg, .jpeg)
PNG (.png)
GIF (.gif)
BMP (.bmp)
WebP (.webp)
Attempting to upload other file types will result in an error: “Invalid file type. Only JPEG, PNG, GIF, JPG, BMP, and WEBP files are allowed.”
File size limits
Maximum file size: 5MB (5,242,880 bytes)
Files exceeding this limit will be rejected with a size limit error.
Storage structure
Files are automatically organized by route:
uploads/
├── profile-photo/ # User profile photos
│ ├── 1678901234567-123456789.jpg
│ └── 1678901234568-987654321.png
└── product/ # Product images
├── 1678901234569-111111111.jpg
├── 1678901234570-222222222.jpg
└── 1678901234571-333333333.png
The middleware extracts the route name from the URL path and creates subdirectories automatically.
Accessing uploaded files
Uploaded files are served statically and accessible via URL:
app . use ( "/uploads" , express . static ( "uploads" ));
Files can be accessed at:
http://localhost:5000/uploads/profile-photo/1678901234567-123456789.jpg
http://localhost:5000/uploads/product/1678901234569-111111111.jpg
Upload examples
Single file upload - Profile photo
Update a user’s profile photo:
Route configuration:
routes/authenticateRoutes.mjs
import { upload } from "../middlewares/fileUpload.mjs" ;
import { updateProfilePhoto } from "../controllers/userController.mjs" ;
import { uploadErrorHandler } from "../handler/responseHandler.mjs" ;
router . post ( "/user/profile-photo" ,
upload . single ( "photo" ),
updateProfilePhoto ,
uploadErrorHandler
);
Controller implementation:
controllers/userController.mjs
export const updateProfilePhoto = async ( req , res ) => {
const userId = req . user . id ;
if ( ! req . file ) {
return errorResponse ({
res ,
statusCode: 400 ,
message: "photo is required" ,
});
}
try {
const imgUrl = ` ${ req . file . destination } / ${ req . file . filename } ` . replaceAll ( ' \\ ' , '/' );
const [ existingUser ] = await db . execute (
"SELECT * FROM users WHERE id = ?" ,
[ userId ]
);
if ( existingUser . length === 0 ) {
return errorResponse ({
res ,
statusCode: 404 ,
message: "User not found" ,
});
}
const [ result ] = await db . execute (
"UPDATE users SET profile_photo = ? WHERE id = ?" ,
[ imgUrl , userId ]
);
successResponse ({
res ,
statusCode: 200 ,
message: "Profile photo updated successfully" ,
data: result ,
});
} catch ( error ) {
console . error ( error );
errorResponse ({
res ,
statusCode: 500 ,
message: "Internal Server Error" ,
});
}
};
cURL example:
curl -X POST http://localhost:5000/api/user/profile-photo \
-H "x-api-key: your-api-key" \
-H "Authorization: Bearer your-jwt-token" \
-F "photo=@/path/to/photo.jpg"
JavaScript fetch example:
const formData = new FormData ();
formData . append ( 'photo' , fileInput . files [ 0 ]);
const response = await fetch ( 'http://localhost:5000/api/user/profile-photo' , {
method: 'POST' ,
headers: {
'x-api-key' : 'your-api-key' ,
'Authorization' : 'Bearer your-jwt-token'
},
body: formData
});
const result = await response . json ();
Multiple file upload - Product images
Add a product with multiple images:
Route configuration:
import { upload } from "../middlewares/fileUpload.mjs" ;
import { addProduct } from "../controllers/productController.mjs" ;
import { uploadErrorHandler } from "../handler/responseHandler.mjs" ;
router . post ( "/product" ,
upload . array ( "images" ),
addProduct ,
uploadErrorHandler
);
Controller implementation:
controllers/productController.mjs
export const addProduct = async ( req , res ) => {
const { name , description , variant , price , stock , categoryId } = req . body ;
if ( ! name || ! description || ! price || ! stock || ! categoryId ) {
return errorResponse ({
res ,
statusCode: 400 ,
message: "All fields are required" ,
});
}
if ( ! req . files || req . files . length === 0 ) {
return errorResponse ({
res ,
statusCode: 400 ,
message: "At least one image file is required" ,
});
}
try {
const imgUrls = req . files . map (( file ) => file . path . replaceAll ( " \\ " , "/" ));
const slug = name
. toLowerCase ()
. replace ( / \s + / g , "-" )
. replace ( /&/ g , "and" )
. replace ( / [ ^ \w- ] / g , "" );
const [ result ] = await db . execute (
"INSERT INTO products (name, slug, description, variant, price, stock, category_id, img_urls) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" ,
[
name ,
slug ,
description ,
JSON . stringify ( parsedVariant ),
price ,
stock ,
categoryId ,
JSON . stringify ( imgUrls ),
]
);
const [ product ] = await db . execute ( "SELECT * FROM products WHERE id = ?" , [
result . insertId ,
]);
successResponse ({
res ,
statusCode: 201 ,
message: "Product added successfully" ,
data: {
... product [ 0 ],
variant: JSON . parse ( product [ 0 ]. variant ),
img_urls: JSON . parse ( product [ 0 ]. img_urls ),
},
});
} catch ( error ) {
console . error ( error );
errorResponse ({ res , statusCode: 500 , message: "Internal Server Error" });
}
};
cURL example:
curl -X POST http://localhost:5000/api/admin/product \
-H "x-api-key: your-admin-api-key" \
-F "name=Premium Laptop" \
-F "description=High-performance laptop" \
-F "price=999.99" \
-F "stock=50" \
-F "categoryId=1" \
-F "images=@/path/to/image1.jpg" \
-F "images=@/path/to/image2.jpg" \
-F "images=@/path/to/image3.jpg"
JavaScript fetch example:
const formData = new FormData ();
formData . append ( 'name' , 'Premium Laptop' );
formData . append ( 'description' , 'High-performance laptop' );
formData . append ( 'price' , '999.99' );
formData . append ( 'stock' , '50' );
formData . append ( 'categoryId' , '1' );
// Add multiple images
for ( let i = 0 ; i < imageFiles . length ; i ++ ) {
formData . append ( 'images' , imageFiles [ i ]);
}
const response = await fetch ( 'http://localhost:5000/api/admin/product' , {
method: 'POST' ,
headers: {
'x-api-key' : 'your-admin-api-key'
},
body: formData
});
const result = await response . json ();
File object properties
When a file is successfully uploaded, the req.file (single) or req.files (multiple) object contains:
{
fieldname : 'photo' ,
originalname : 'profile.jpg' ,
encoding : '7bit' ,
mimetype : 'image/jpeg' ,
destination : 'uploads/profile-photo' ,
filename : '1678901234567-123456789.jpg' ,
path : 'uploads/profile-photo/1678901234567-123456789.jpg' ,
size : 245678
}
Error handling
The API includes an upload error handler middleware:
router . post ( "/user/profile-photo" ,
upload . single ( "photo" ),
updateProfilePhoto ,
uploadErrorHandler // Catches upload errors
);
Common errors:
File type error
File size error
Missing file error
Missing images error
{
"error" : "Invalid file type. Only JPEG, PNG, GIF, JPG, BMP, and WEBP files are allowed." ,
"statusCode" : 400
}
Best practices
Validate on client side
Check file type and size before uploading to improve user experience: const file = input . files [ 0 ];
const maxSize = 5 * 1024 * 1024 ; // 5MB
const allowedTypes = [ 'image/jpeg' , 'image/png' , 'image/gif' , 'image/webp' ];
if ( file . size > maxSize ) {
alert ( 'File too large. Maximum size is 5MB.' );
return ;
}
if ( ! allowedTypes . includes ( file . type )) {
alert ( 'Invalid file type. Please upload an image.' );
return ;
}
Optimize images
Compress images before upload to reduce bandwidth and storage: import imageCompression from 'browser-image-compression' ;
const options = {
maxSizeMB: 1 ,
maxWidthOrHeight: 1920 ,
useWebWorker: true
};
const compressedFile = await imageCompression ( file , options );
Show upload progress
Provide feedback during upload: const xhr = new XMLHttpRequest ();
xhr . upload . addEventListener ( 'progress' , ( e ) => {
if ( e . lengthComputable ) {
const percentComplete = ( e . loaded / e . total ) * 100 ;
console . log ( `Upload progress: ${ percentComplete } %` );
}
});
Clean up old files
Implement a cleanup strategy to remove old or unused files to save storage space.
Consider cloud storage
For production, consider using cloud storage services like AWS S3, Google Cloud Storage, or Cloudinary for better scalability and CDN support.
Troubleshooting
Upload directory permissions
Ensure the application has write permissions for the uploads directory:
Path separator issues
The code handles Windows path separators:
const imgUrl = ` ${ req . file . destination } / ${ req . file . filename } ` . replaceAll ( ' \\ ' , '/' );
This ensures consistent forward slashes in URLs across all platforms.
Missing files in response
If uploaded files aren’t accessible, verify the static file serving is configured:
app . use ( "/uploads" , express . static ( "uploads" ));
Next steps
Products API Learn how to manage products with images
User API Update user profiles with photos