This error was shown when i send other data along with photo:
Image upload failed: Server returned unexpected status code:403
Actually, I was practicing a pern stack backend project using prisma ORM. i used cloudinary and multer for registration of user with profile image. Without images the endpoints were working fine but when i started using images then the error started coming.
Here is the code of multer.middleware.js:
import multer from "multer";
import fs from "fs";
import path from "path";
import { ApiError } from "../utils/ApiError.js";
// Temporary holding area of files before going to Cloudinary.
const tempDir = path.resolve("temp/uploads");
fs.mkdirSync(tempDir, { recursive: true });
const storage = multer.diskStorage({
destination: (req, file, cb) =\> cb(null, tempDir),
filename: (req, file, cb) =\> {
const uniqueSuffix = \`${Date.now()}-${Math.round(Math.random() \* 1e9)}\`;
cb(null, \`${uniqueSuffix}${path.extname(file.originalname)}\`);
},
});
const ALLOWED_MIME_TYPES = [
"image/jpeg",
"image/jpg",
"image/png",
"image/webp",
];
const fileFilter = (req, file, cb) => {
if (!ALLOWED_MIME_TYPES.includes(file.mimetype)) {
return cb(
new ApiError(400, "Only JPG, PNG, or WEBP images are allowed")
);
}
cb(null, true);
};
const upload = multer({
storage,
fileFilter,
limits: { fileSize: 5 \* 1024 \* 1024 }, // 5MB
});
export { upload };
And the cloudinary.js code:
import { v2 as cloudinary } from "cloudinary";
import fs from "fs";
import { ApiError } from "./ApiError.js";
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
const uploadOnCloudinary = async (localFilePath, folder = "WaitLess") => {
if (!localFilePath) return null;
try {
const result = await cloudinary.uploader.upload(localFilePath, {
folder,
resource_type: "image",
});
return { url: result.secure_url, publicId: result.public_id };
} catch (error) {
throw new ApiError(500, \`Image upload failed: ${error.message}\`);
} finally {
fs.unlink(localFilePath, (err) =\> {
if (err) {
console.error("Failed to delete temporary file:", err.message);
}
});
}
};
const deleteFromCloudinary = async (publicId) => {
if (!publicId) return;
try {
await cloudinary.uploader.destroy(publicId);
} catch (error) {
console.error(
\`Failed to delete Cloudinary asset "${publicId}":\`,
error.message
);
}
};
export { uploadOnCloudinary, deleteFromCloudinary };
The user.service.js code:
import { ApiError } from "../utils/ApiError.js";
import bcrypt from "bcryptjs";
import { prisma } from "../utils/prisma.js";
import { deleteFromCloudinary } from "../utils/cloudinary.js";
import { generateOtp, hashOtp, getOtpExpiry } from "../utils/otp.js";
import { sendEmail } from "../utils/email.js";
const ROLES = ["USER", "STAFF", "ADMIN"];
const safeUserSelect = {
id: true,
fullName: true,
email: true,
phoneNumber: true,
address: true,
profileImage: true,
role: true,
createdAt: true,
updatedAt: true,
};
// REGISTER USER
const registerUserService = async ({
fullName,
email,
password,
phoneNumber,
address,
profileImage,
profileImageId,
role,
}) => {
const normalizedEmail = email.trim().toLowerCase();
const existingUser = await prisma.user.findUnique({
where: {
email: normalizedEmail,
},
});
if (existingUser) {
throw new ApiError(409, "Email already exists");
}
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = await prisma.user.create({
data: {
fullName: fullName.trim(),
email: normalizedEmail,
password: hashedPassword,
phoneNumber: phoneNumber.trim(),
address: address.trim(),
profileImage,
profileImageId,
role: ROLES.includes(role) ? role : "USER",
},
select: safeUserSelect,
});
return newUser;
};
And the user.controller.js code:
import { ApiResponse } from "../utils/ApiResponse.js";
import * as userService from "../services/user.service.js";
import { generateToken } from "../utils/generateToken.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import { uploadOnCloudinary } from "../utils/cloudinary.js";
// CREATE/REGISTER USER
const registerUser = asyncHandler(async (req, res) => {
let uploadedImage = null;
if (req.file) {
uploadedImage = await uploadOnCloudinary(
req.file.path,
"WaitLess/users"
);
}
const requestedRole = req.body.role;
const isAdminCaller = req.user?.role === "ADMIN";
const resolvedRole = isAdminCaller ? requestedRole : undefined;
const user = await userService.registerUserService({
...req.body,
role: resolvedRole,
profileImage: uploadedImage?.url,
profileImageId: uploadedImage?.publicId,
});
const token = generateToken(user.id, res);
return res.status(201).json(
new ApiResponse(
201,
{
user,
token,
},
"User registered successfully"
)
);
});
And finally the user.route.js code:
import { Router } from "express";
import {
registerUser,
loginUser,
updateUser,
deleteUser,
getOwnProfile,
getAllUsers,
getUserById,
logout,
forgotPassword,
resetPassword,
} from "../controllers/user.controller.js";
import {
authMiddleware,
optionalAuth,
authorizeRoles,
} from "../middlewares/auth.middleware.js";
import {
validateUserRegister,
validateUserLogin,
validateUserUpdate,
validateForgotPassword,
validateResetPassword,
} from "../validators/user.validator.js";
import { validate } from "../middlewares/validate.middleware.js";
import { upload } from "../middlewares/multer.middleware.js";
const router = Router();
router.post(
"/register",
optionalAuth,
upload.single("profileImage"),
validate(validateUserRegister),
registerUser
);
router.post("/login", validate(validateUserLogin), loginUser);
router.post(
"/forgot-password",
validate(validateForgotPassword),
forgotPassword
);
router.post("/reset-password", validate(validateResetPassword), resetPassword);
router.get(
"/all-lists",
authMiddleware,
authorizeRoles("ADMIN", "STAFF"),
getAllUsers
);
router.get("/profile", authMiddleware, getOwnProfile);
router.post("/logout", authMiddleware, logout);
router.patch(
"/update/:id",
authMiddleware,
upload.single("profileImage"),
validate(validateUserUpdate),
updateUser
);
router.delete("/delete/:id", authMiddleware, deleteUser);
router.get(
"/info/:id",
authMiddleware,
authorizeRoles("ADMIN", "STAFF"),
getUserById
);
export default router;