MERN Stack App - User Avatar Upload - 500 Error After Deployment on Render

25 views Asked by At

I have a MERN stack application which I deployed to Render (server side as web service and front end as static).
In my application, I have a profile where users can upload avatar image. I store an image as a URL string in my MongoDB Atlas_ in User model.

Images are saved on the server in uploads folder. The image upload works fine and image fetches successfully, from both deployment and local environment. But after a while I start to get Internal server error when fetching same image.

What is the reason and how to solve this issue? Or is there a better way of storing images in MongoDB Atlas without using cloud or GridFS?

Here's how I set the user avatar:

const setUserAvatar = async (req, res) => {
    try {
        if (!req.user || !req.user.id) {
            return res.status(401).json({ message: 'Unauthorized' });
        }
        const user = await User.findById(req.user.id);
        if (!user) {
            return res.status(404).json({ message: 'User not found' });
        }

        if (!req.file) {
            return res.status(400).json({ message: 'No file uploaded' });
        }

        const fileName = `avatar_${user.id}_${Date.now()}.png`;
        const filePath = path.join(__dirname, '..', 'uploads', fileName);

        fs.writeFileSync(filePath, req.file.buffer);

        user.avatar = { data: fileName };
        await user.save();


        res.status(200).json({ message: 'Avatar uploaded successfully' });

    } catch (error) {
        console.error('Error uploading avatar', error);
        res.status(500).json({ message: 'Internal Server Error' });

    }
};

And get:

const getUserAvatar = async (req, res) => {
    const userId = req.user.id;
    try {
        const user = await User.findById(userId);
        if (user.avatar.data === null) {
            return res.status(404).json({ message: 'No avatar found' })
        }
        if (!user || !user.avatar.data) {
            res.send(user.avatar.data)
        } else {
            const filePath = path.join(__dirname, '..', 'uploads', user.avatar.data);
            const avatarData = fs.readFileSync(filePath);
            res.setHeader('Content-Type', 'image/*');
            res.send(avatarData);
        }

    } catch (error) {
        console.error('Error fetching avatar', error);
        res.status(500).json({ message: 'Internal Server Error' });
    }
};

How I handle an avatar change on the front end:


   const handleAvatarChange = async (e) => {
        const token = localStorage.getItem('token');
        if (token) {
            try {
                const formData = new FormData();
                formData.append('avatar', e.target.files[0])
                const response = await fetch('RENDERLINK/avatar', {
                    'method': 'POST',
                    'headers': {
                        'Authorization': `Bearer ${token}`,
                    },
                    'body': formData,
                    'credentials': 'include'
                })
                if (response.status === 200) {
                    getUser()
                    toast.success('Avatar changed successfully')
                } else if (response.status === 400) {
                    toast.error('No file selected')
                } else {
                    toast.error('Error changing avatar')
                }
            } catch (error) {
                toast.error('Oops! Something went wrong. Please try again later.')
            }
        } else {
            console.log('No token found')
            navigate('/login')
        }
    }

Fetching user data including the avatar:

    const getUser = async () => {
        const token = localStorage.getItem('token');
        if (token) {
            try {
                const response = await fetch('RENDERLINK/profile', {
                    'method': 'POST',
                    'headers': {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${token}`,
                    },
                    'credentials': 'include'
                })

                if (response.ok) {
                    const data = await response.json();

                    setUser({ username: data.username, email: data.email, avatar: data.avatar.data })
                    if (data.avatar.data !== null) {
                        // Fetch and set the avatar image
                        const avatarResponse = await fetch('RENDERLINK/get_avatar', {
                            headers: {
                                'Authorization': `Bearer ${token}`,
                            },
                            credentials: 'include',
                        });
                        const avatarBlob = await avatarResponse.blob();
                        const avatarUrl = URL.createObjectURL(avatarBlob);
                        setUser((prevUser) => ({ ...prevUser, avatar: avatarUrl }));
                        setLoading(false)
                    }

                } else if (response.status === 500) {
                    toast.error('Oops! Something went wrong. Please try again later.')
                }

            } catch (error) {
                toast.error('Oops! Something went wrong. Please try again later.')
            }
        } else {
            console.log('No token found')
            navigate('/login')
        }
    }
0

There are 0 answers