I am getting an email from lstate which is a state of useReducer. I am getting lstate value by using Context api and useContext. But my problem is that i transfered the lstate using fetch api to my backend auth.js to get the document of the required email from the MongoDB. But i am not getting email from lstate at backend i am getting objects.
Account.jsx
const Account = () => {
const { lstate } = useContext(LoginContext);
const LoginData = async () => {
console.log(`my lstate in login function ${lstate}`);
await fetch('/account', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
lstate
})
}).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
})
}
useEffect(() => {
LoginData();
}, []);
auth.js
router.post('/account', async (req, res) => {
try {
const email = req.body;
console.log(`data from account ${email}`);
const data = await CustomerModel.findOne({ email: email });
if (data) {
return res.json(data);
}
} catch (error) {
console.log(error);
}
})
Console Error
data from account [object Object]
CastError: Cast to string failed for value "{ lstate: 'usman4276#gmail.com' }" at path "email" for model "Customer_reg"
at model.Query.exec (C:\MERN stack\online-cattle-shop\backend\node_modules\mongoose\lib\query.js:4408:21)
at model.Query.Query.then (C:\MERN stack\online-cattle-shop\backend\node_modules\mongoose\lib\query.jsbackend\node_modules\mongoose\lib\query.js:4502:15)
at processTicksAndRejections (internal/process/task_queues.js:93:5) {
messageFormat: undefined,
stringValue: `"{ lstate: 'usman4276#gmail.com' }"`, kind: 'string',
value: { lstate: 'usman4276#gmail.com' },
path: 'email',
reason: null
}
should be
const { email } = req.body;
OR
const email = req.body.email;
You should also add conditional checks.
E.g.
if (!email) throw new Error ('Email Required')
The code in the client is sending a JSON and the key lstate contains the actual mail.
you need to do
const email = req.body.email.lstate;
const data = await CustomerModel.findOne({ email });
Related
In my backend is something happening, which I can't understand. If I'm registering a new User, it's working fine, and I can see the new User in my JSON File, but if I'm doing a put request after that to change my own user's data he deletes the new User which I made before?
My put request from my frontend:
//Changing user Data
export async function changeData(id, body) {
try {
await axios.put(`http://localhost:8000/users/${id}`, body, {
headers: {
'Content-Type': 'application/json',
'Authorization': localStorage.getItem('auth._token.local')
}
});
return true;
}
catch (e) {
return false;
}
}
My endpoint in my node backend for registering a user and changing data of a user
// Register New User
server.post('/register', (req, res) => {
console.log("register with request body", req.body)
const {username, password, firstname, lastname, roles} = req.body
if(!username || !password || !firstname || !lastname || !roles) {
const status = 400
const message = "Bad Request, make sure all properties are set in request body"
res.status(status).json({status, message})
return
}
if (req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer') {
const status = 401
const message = 'Error in authorization format'
res.status(status).json({status, message})
return
}
// Send only token part to admin check
if(!isAdmin(req.headers.authorization.split(' ')[1])) {
const status = 401
const message = 'Only permitted by admin'
res.status(status).json({status, message})
return
}
if(isAuthenticated({username, password}) === true) {
const status = 401
const message = 'Email and Password already exist'
res.status(status).json({status, message})
return
}
fs.readFile("./users.json", (err, file) => {
if (err) {
const status = 401
const message = err
res.status(status).json({status, message})
return
}
// Get current users data
const data = JSON.parse(file.toString())
// Get the id of last user
const last_item_id = data.users[data.users.length - 1].id
//Add new user
data.users.push({id: last_item_id + 1, username: username, password: password, firstname: firstname, lastname: lastname, roles: roles}) //add some data
const writeData = fs.writeFile("./users.json", JSON.stringify(data), (err, result) => { // WRITE
if (err) {
const status = 401
const message = err
res.status(status).json({status, message})
}
})
})
res.status(201).json({status: 201, message: "Successfully created"})
})
// handle changing user data
server.use((req, res, next) => {
console.log('Entering Users')
if(req.method === 'PUT' && req.url.includes("/users")) {
if(req.body) {
const decodedToken = jwt.decode(req.headers.authorization.split(' ')[1])
const userList = JSON.parse(fs.readFileSync('./users.json', 'UTF-8'))
const userinfo = userList.users.find((user) => user.id === decodedToken.id)
if(!req.body.password) {
req.body.password = userinfo.password
}
// if admin made the request, he should be able to change roles
if(req.body.roles && decodedToken.roles && decodedToken.roles.includes("admin")) {
console.log("Able to change");
next()
return
}
req.body.roles = decodedToken.roles
} else {
res.status(400).json(
{
status: 400,
message: "Bad request, make sure all properties are set in request body"
}
)
return
}
}
next()
})
The only thing i noticed is that after the register comes, the JSON file gets to a one-liner, but I don't think that this is the problem. It seems like the put works with an old user List? I'm not sure. Thanks in forward.
I have create backend using express and mongodb database. I am trying to fetch data in react but getting an error while fetching the data as show. Please can anyone tell what the solution of above error is and how can i fetch data from the backend
const Register = () => {
const [values, setValues] = useState({
name: "",
age: "",
country: "",
email: "",
});
const setData = (e) => {
console.log(e.target.value);
const { name, value } = e.target;
setValues((val) => {
return {
...val,
[name]: value,
};
});
};
const addData = async (e) => {
e.preventDefault();
const { name, age, country, email } = values;
const res = await fetch("/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
age,
country,
email,
}),
});
const data = await res.json();
console.log(data);
if (res.status === 404 || !data) {
console.log("Error");
} else {
console.log("Data added successfully");
}
};
Here below is the backend code where the post function is performed.
router.post("/register", async (req, res) => {
const { name, age, country, email } = req.body;
if (!name || !age || !country || !email) {
res.status(404).send("Some data is missing");
}
try {
const preuser = await Crud.findOne({ email: email });
console.log(preuser);
if (preuser) {
res.status(404).send("The user already exists");
} else {
let addUser = new Crud({
name,
age,
country,
email,
});
addUser = await addUser.save();
res.status(201).json(addUser);
console.log(addUser);
}
} catch (error) {
res.status(404).send(error);
}
});
await fetch leads to an exception when the HTTP status is ≥ 400. You must add a try-catch block to handle such exceptions:
try {
const res = await fetch("/register", {...});
} catch(exception) {
// Handle the exception
}
Also, HTTP status 404 should be used when a resource is not found. You use it when a user already exists (where status 400 would be more appropriate) or in case of a database error (when 500 would be more appropriate).
I am working on an Application which i have also deployed in heroku. The issue is that when I login in using heroku, user is nested inside a data object. but when I work locally or use postman, user isnt nested.
Help Please.
I get this response on the deployed version.
data: {
user: {
email: "my_email"
name: "my_name"
role: "user"
_id: "6205807deeadcfa734f954f3".
}
status: "success"
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyMDU4MDdkZWVhZGNmYTczNGY5NTRmMyIsImlhdCI6MTY0NDg0NTYyMCwiZXhwIjoxNjQ1NDUwNDIwfQ.YeWFNrN8rsLPJvvU8JQDwBVG4aBqqEuo7ssgLrR3O8M"
But when I log in locally, I get the response as
user: {
email: "my_email"
name: "my_name"
role: "user"
_id: "6205807deeadcfa734f954f3".
}
status: "success"
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyMDU4MDdkZWVhZGNmYTczNGY5NTRmMyIsImlhdCI
For Heroku, the USER is nested inside data but for local host and postman, the user isnt nested.
My codes are:
exports.login = catchAsync(async (req, res, next) => {
const { email, password } = req.body
if (!email || !password) {
return next(new AppError('Please provide email and password!', 400))
}
const user = await User.findOne({ email }).select('+password')
if (!user || !(await user.comparePassword(password, user.password))) {
return next(new AppError('Incorrect email or password', 401))
}
createSendToken(user, 200, req, res)
})
These are my api codes
const createSendToken = (user, statusCode, req, res) => {
const token = signToken(user._id)
res.cookie('jwt', token, {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000
),
httpOnly: true,
})
user.password = undefined
res.status(statusCode).json({
status: 'success',
token,
user,
})
}
For my react, The function code is:
function request(path, { data = null, token = null, method = 'GET' }) {
return (
fetch(`${process.env.REACT_APP_API}${path}`, {
method,
headers: {
Authorization: token ? `Bearer ${token}` : '',
'Content-Type': 'application/json',
},
body:
method !== 'GET' && method !== 'DELETE' ? JSON.stringify(data) : null,
})
.then((response) => {
// If Successful
if (response.ok) {
if (method === 'DELETE') {
// If delete, nothing returned
return true
}
return response.json()
}
// If errors
return response
.json()
.then((json) => {
// Handle Json Error response from server
if (response.status === 400) {
const errors = Object.keys(json).map(
(k) => `${json[k].join(' ')}`
)
throw new Error(errors.join(' '))
}
throw new Error(JSON.stringify(json))
})
.catch((e) => {
if (e.name === 'SyntaxError') {
throw new Error(response.statusText)
}
throw new Error(e)
})
})
.catch((e) => {
// Handle all errors
toast(e.message, { type: 'error' })
})
)
}
The main sign in function
export function signIn(email, password) {
return request('/api/v1/auth/login', {
data: { email, password },
method: 'POST',
})
}
Then I import this into my auth context and execute it there
import {signIn as signInApi} from '../apis'
const AuthContext = createContext()
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(localStorage.getItem('token'))
const [user, setUser] = useState(
JSON.parse(localStorage.getItem('user'))
)
const [loading, setLoading] = useState(false)
const signIn = async (email, password, callback) => {
setLoading(true)
const res = await signInApi(email, password)
if (res.token) {
localStorage.setItem('token', res.token)
localStorage.setItem('user', JSON.stringify(res.user)) // This stores the user in localhost but returns undefined for user in the one deployed to heroku. I have to use
localStorage.setItem('user', JSON.stringify(res.data.user)) which now works on the deployed one but not on the local one
setToken(res.token)
setUser(res.user)
callback()
}
setLoading(false)
}
}
it seems the deployed version is using built in implementaion of createSendToken and not the one you provided. need to check your project structure.
in order to validate this change the function name and the call createSendToken to something else and you will find the issue
Hello I want to find posts which user has made ..
I do my request with JWT Token:
###
http://localhost:8080/forum/getByOwnerID
Authorization: Bearer {{token}}
This is my create function :
exports.create = async (req, res) => {
const { forumName, forumDescription } = req.body;
const token = req.token;
const forumExist = await Forum.findOne({ forumName: req.body.forumName });
if(forumExist){
res.status(400).send("Forum Exists already.");
}
try{
const owner = await User.findOne({userID:token._id});
if (!forumName || !forumDescription) {
res.status(400);
throw new Error("Please Fill all the feilds");
return;
}
else {
const newForum = new Forum({ forumName, forumDescription,user: owner.userID });
newForum.user = owner;
const createdNote = await newForum.save();
res.status(201).json(createdNote);
}
}catch(err){
res.status(400).send(err);
}
};
This is my function where I want to get the Posts which the user has made :
exports.getByToken = async (req, res, next) => {
const forum = await Forum.findById( {user: req.token._id} );
if (forum) {
res.json(forum);
} else {
res.status(404).json({ message: "Forum not found" });
}
res.json(forum);
}
And this is model which I have for Post:
const forumSchema = ({
forumName: {
type: String,
required: true,
},
forumDescription: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
published_on: {
type: String,
default: moment().format("LLL")
},
});
Everytime I do a request it has this error :
UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value "{ user: 'admin' }" (type Object) at path "_id" for model "Forum"
my generate Token :
const generateToken = (_id, userID) => {
console.log('Signing token for ID ', _id,userID);
console.log('Secret key is ', process.env.JWT_KEY);
const token = jwt.sign({ _id,userID}, process.env.JWT_KEY, {
expiresIn: "30d",
});
console.log('Signed token: ', token);
return token;
};
As you are using findById, you should only send the id as argument function.
If you want to search with filter query, use find method
I am creating a MERN app, I am new to this. I tried following some tutorials on the net but I have encountered some error.
I am submitting the post request like this. As you can see I am not specifying the content type because I know that if you are using the 'multipart/form-data it will automatically append it to the headers. I am using a react hook here which is why I am not directly using the fetch method.
const formData = new FormData();
formData.append("email", formState.inputs.email.value);
formData.append("name", formState.inputs.name.value);
formData.append("password", formState.inputs.password.value);
formData.append("image", formState.inputs.image.value);
const responseData = await sendRequest(
`${process.env.REACT_APP_BACKEND_BASE_URL}/api/users/signup`,
"POST",
formData
);
meanwhile my signup route is like this, UNDER user-routes.js
router.post(
"/signup",
fileUpload.single("image"),
[
check("name").not().isEmpty(),
check("email")
.normalizeEmail() // Test#test.com => test#test.com
.isEmail(),
check("password").isLength({ min: 6 }),
],
usersController.signup
);
as you can also see, I am catching it in the route by using the fileUpload.single("image")
if you need to see my hook that I am using here it is, but I am pretty sure that the hook works fine. and it has no issues whatsoever, so here it is: this is a react hook
export const useHttpClient = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
const activeHttpRequests = useRef([]);
const sendRequest = useCallback(
async (url, method = "GET", body = null, headers = {}) => {
setIsLoading(true);
const httpAbortCtrl = new AbortController();
activeHttpRequests.current.push(httpAbortCtrl);
try {
const response = await fetch(url, {
method,
body,
headers,
signal: httpAbortCtrl.signal,
});
const responseData = await response.json();
// console.log("Response: ", response);
// console.log("Data: ", responseData);
activeHttpRequests.current = activeHttpRequests.current.filter(
(reqCtrl) => reqCtrl !== httpAbortCtrl
);
if (!response.ok) {
throw new Error(responseData.message);
}
setIsLoading(false);
return responseData;
} catch (err) {
setError(err.message);
setIsLoading(false);
throw err;
}
},
[]
);
const clearError = () => {
setError(null);
};
useEffect(() => {
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
activeHttpRequests.current.forEach((abortCtrl) => abortCtrl.abort());
};
}, []);
return { isLoading, error, sendRequest, clearError };
};
I will include the signup here from my users.controller:
const signup = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return next(
new HttpError("Invalid inputs passed, please check your data.", 422)
);
}
const { name, email, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
const error = new HttpError(
"Signing up failed, please try again later.",
500
);
return next(error);
}
if (existingUser) {
const error = new HttpError(
"User exists already, please login instead.",
422
);
return next(error);
}
let hashedPassword;
try {
hashedPassword = await bcrypt.hash(password, 12);
} catch (err) {
const error = new HttpError(
"Could not create user, please try again.",
500
);
return next(error);
}
//res.json({ message: "AFTER HASHING" });
const createdUser = new User({
name,
email,
image: req.file.path,
password: hashedPassword,
places: [],
});
try {
await createdUser.save();
} catch (err) {
const error = new HttpError(
"Signing up failed, please try again later.",
500
);
return next(error);
}
let token;
try {
token = jwt.sign(
{ userId: createdUser.id, email: createdUser.email },
process.env.JWT_KEY,
{ expiresIn: "1h" }
);
} catch (err) {
const error = new HttpError(
"Signing up failed, please try again later.",
500
);
return next(error);
}
res
.status(201)
.json({ userId: createdUser.id, email: createdUser.email, token: token });
};
You may want to use JSON.Parse() around your responseData
When ever I run into this bug it is because I either,
parsed an already parsed object
Stingified a string
Used a string as an object
This link goes into more depth about the bug and common ways of dissecting the issue.
Try console.log() in your hook before you send the data and also log what the responseData looks like after it retrieves the data