Here are the Steps: –
- Add route – router.put(‘/update/:userId’, verifyToken, updateUser);
- Install a package – npm i cookie-parser
- Import cookie parser in index.js – import cookieParser from ‘cookie-parser’
- Use cookie parser – app.use(cookieParser());
- Create a file inside utils “verifyUser.js”
import jwt from ‘jsonwebtoken’
import { errorHandler } from ‘./error.js’;
export const verifyToken=(req, res, next)=>{
const token=req.cookies.access_token;
if(!token){
return next(errorHandler(401, “Unauthorized”))
}
jwt.verify(token, process.env.JWT_SECRET_KEY, (err, user)=>{
if(err){
return next(errorHandler(401, ‘Unauthorized’))
}
req.user=user;
next();
})
}
6. Create user.controller.js –
import bcryptjs from ‘bcryptjs’;
import { errorHandler } from ‘../utils/error.js’;
import User from ‘../models/user.model.js’;
export const test=(req, res)=>{
res.json({message:“API is working”})
}
export const updateUser=async(req, res, next)=>{
if(req.user.id !== req.params.userId){
return next(errorHandler(403, ‘You are not allowed to update this user’))
}
if(req.body.password){
if(req.body.password.length<6){
return next(errorHandler(400, ‘Password must be at least 6 characters’))
}
req.body.password=bcryptjs.hashSync(req.body.password, 10);
}
if(req.body.username){
if(req.body.username.length<7 || req.body.username.length>20){
return next(errorHandler(400, ‘Username must be between 7 and 20 characters’))
}
if(req.body.username.includes(‘ ‘)){
return next(errorHandler(400, ‘Username cannot contain space’))
}
if(req.body.username !==req.body.username.toLowerCase()){
return next(errorHandler(400, ‘Username must be in lower case’))
}
if(!req.body.username.match(/^[a-zA-Z0-9]+$/)){
return next(errorHandler(400, “Username can only contain charcters and numbers”))
}
}
try {
const updatedUser=await User.findByIdAndUpdate(req.params.userId,{
$set:{
username:req.body.username,
email:req.body.email,
profilePicture:req.body.profilePicture,
password:req.body.password
}
}, {new:true})
const {password, …rest}=updatedUser._doc;
res.status(200).json(rest);
} catch (error) {
next(error)
}
}
Implement Update User Profile Page API –
DashProfile.jsx:-
import React, { useEffect, useRef, useState } from ‘react’
import { Alert, Button, TextInput } from ‘flowbite-react’
import {useSelector} from ‘react-redux’
import {app} from ‘../firebase’
import {getDownloadURL, getStorage, ref, uploadBytesResumable} from ‘firebase/storage’
import { CircularProgressbar } from ‘react-circular-progressbar’;
import ‘react-circular-progressbar/dist/styles.css’;
import {updateStart, updateSuccess, updateFailure} from ‘../redux/user/userSlice.js’
import {useDispatch} from ‘react-redux’
const DashProfile = () => {
const {currentUser}=useSelector(state=>state.user)
const [imageFile, setImageFile]=useState(null)
const [imageFileUrl, setImageFileUrl]=useState(null);
const [imageFileUploadProgress, setImageFileUploadProgress]=useState(null);
const [imageFileUploadError, setImageFileUploadError]=useState(null);
const [imageFileUploading, setImageFileUploading]=useState(false)
const [updateUserSuccess, setUpdateUserSuccess]=useState(null)
const [updateUserError, setUpdateUserError]=useState(null)
const [formData, setFormData]=useState({});
const dispatch=useDispatch();
console.log(imageFileUploadProgress, imageFileUploadError);
const filePickerRef=useRef();
const handleImageChange=(e)=>{
const file=e.target.files[0];
if(file){
setImageFile(file);
setImageFileUrl(URL.createObjectURL(file));
}
};
useEffect(()=>{
if(imageFile){
uploadImage();
}
}, [imageFile])
const uploadImage=async()=>{
// service firebase.storage {
// match /b/{bucket}/o {
// match /{allPaths=**} {
// allow read;
// allow write: if
// request.resource.size<2*1024*1024 &&
// request.resource.contentType.matches(‘image/.*’);
// }
// }
// }
setImageFileUploading(true)
setImageFileUploadError(null)
const storage=getStorage(app);
const fileName=new Date().getTime()+imageFile.name;
const storageRef=ref(storage, fileName);
const uploadTask=uploadBytesResumable(storageRef, imageFile);
uploadTask.on(
‘state_changed’,
(snapshot)=>{
const progress=(snapshot.bytesTransferred/snapshot.totalBytes)*100;
setImageFileUploadProgress(progress.toFixed(0));
},
(error)=>{
setImageFileUploadError(‘Could Not Upload Image (File must be less than 2MB)’)
setImageFileUploadProgress(null)
setImageFile(null);
setImageFileUrl(null)
setImageFileUploading(false)
},
()=>{
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL)=>{
setImageFileUrl(downloadURL);
setFormData({…formData, profilePicture:downloadURL});
setImageFileUploading(false)
})
}
)
console.log(“Uploading Image…”)
}
console.log(imageFile, imageFileUrl)
const handleChange=(e)=>{
setFormData({…formData, [e.target.id]:e.target.value})
}
console.log(formData)
const handleSubmit=async(e)=>{
e.preventDefault();
setUpdateUserError(null);
setUpdateUserSuccess(null)
if(Object.keys(formData).length===0){
setUpdateUserError(“No Changes made”)
return;
}
if(imageFileUploading){
setUpdateUserError(‘Please wait for image to upload’)
return;
}
try {
dispatch(updateStart());
const res=await fetch(`/api/user/update/${currentUser._id}`, {
method:‘PUT’,
headers:{
‘Content-Type’:‘application/json’
},
body:JSON.stringify(formData)
})
const data=await res.json();
if(!res.ok){
dispatch(updateFailure(data.message))
setUpdateUserError(data.message)
}else{
dispatch(updateSuccess(data))
setUpdateUserSuccess(“User’s Profile Updated Successfully”)
}
} catch (error) {
dispatch(updateFailure(error.message))
setUpdateUserError(error.message)
}
}
return (
<div className=‘ max-w-lg mx-auto p-3 w-full’>
<h1 className=‘my-7 text-center font-semibold text-3xl’>Profile</h1>
<form className=‘flex flex-col gap-4’ onSubmit={handleSubmit}>
<input type=‘file’ accept=‘image/*’ onChange={handleImageChange} ref={filePickerRef}/>
<div className=‘ relative w-32 h-32 self-center cursor-pointer rounded-full shadow-md overflow-hidden’ onClick={()=>filePickerRef.current.click()}>
{
imageFileUploadProgress && (
<CircularProgressbar value={imageFileUploadProgress || 0} text={`${imageFileUploadProgress}%`} strokeWidth={5} styles={{
root:{
width:‘100%’,
height:‘100%’,
position:‘absolute’,
top:0,
left:0
},
path:{
stroke:`rgba(62, 152, 199, ${imageFileUploadProgress/100})`
}
}}/>
)
}
<img src={imageFileUrl || currentUser.profilePicture} alt=‘user’ className={` rounded-full w-full h-full object-cover border-8 border-[lightgray] ${imageFileUploadProgress && imageFileUploadProgress<100 && ‘opacity-60’}`}/>
</div>
{
imageFileUploadError && <Alert color=‘failure’>{imageFileUploadError}</Alert>
}
<TextInput type=‘text’ id=‘username’ placeholder=‘username’ defaultValue={currentUser.username} onChange={handleChange}/>
<TextInput type=’email’ id=’email’ placeholder=’email’ defaultValue={currentUser.email} onChange={handleChange}/>
<TextInput type=‘password’ id=‘password’ placeholder=‘password’ onChange={handleChange}/>
<Button type=‘submit’ gradientDuoTone=‘purpleToBlue’ outline>Update</Button>
</form>
<div className=‘ text-red-500 flex justify-between gap-5’>
<span className=‘ cursor-pointer’>Delete Account</span>
<span className=‘ cursor-pointer’>Sign Out</span>
</div>
{
updateUserSuccess && (
<Alert color=‘success’ className=‘mt-5’>
{updateUserSuccess}
</Alert>
)
}
{
updateUserError && (
<Alert color=‘failure’ className=‘mt-5’>
{updateUserError}
</Alert>
)
}
</div>
)
}
export default DashProfile
UserSlice.jsx:
import {createSlice} from ‘@reduxjs/toolkit’
const initialState={
currentUser:null,
error:null,
loading:false
}
const userSlice=createSlice({
name:‘user’,
initialState,
reducers:{
signInStart:(state)=>{
state.loading=true,
state.error=null
},
signInSuccess:(state, action)=>{
state.currentUser=action.payload,
state.loading=false,
state.error=null
},
signInFailure:(state, action)=>{
state.loading=false,
state.error=action.payload
},
updateStart:(state)=>{
state.loading=true;
state.error=null
},
updateSuccess:(state, action)=>{
state.currentUser=action.payload;
state.loading=false;
state.error=null
},
updateFailure:(state, action)=>{
state.loading=false;
state.error=action.payload;
}
}
})
export const {signInStart, signInSuccess, signInFailure, updateStart, updateSuccess, updateFailure} =userSlice.actions;
export default userSlice.reducer;
Video Tutorial – Create Update User API Route in React JS
Video Tutorial – implement Update User Profile Page API