react & node API - fetch function error - node.js

I'm just new in React and NodeJS express programming and I'm stuck in some error for hours.
when I'm trying to fetch, sometimes I got a response (but not the my response)
and sometimes I got 'failed to fetch'.
I have tried to understand 'cors' concept without a lot of understanding,
but I have commit app.use(cors()) that allowed every source.
note that my server sometimes got 'OPTIONS' request, and sometimes getting 'POST' request.
in my React, I have this function:
fetch('http://localhost:3000/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify ({
user: this.state.username,
pass: this.state.password,
})
}).then(res => {
console.log(res.msg);
})
.catch(function(err) {
alert(err);
})
}
in my NodeJS I have this function:
router.post('/', (req, res) => {
let user = {
username: req.body.user,
password: req.body.pass
}
UserModel.findOne({'username': user.username, 'password' : user.password}, function(err, user) {
if (err) {
res.json({
ok: false,
msg: 'login failed, try later'
});
} else if (user) {
let token = jwt.encode(user, secret);
res.json({
msg: 'logged in',
ok: true
});
} else {
res.json({
ok: false,
msg: 'invalid input'
});
}
});
Im very confused, I hope you can help me.
Thanks.

You aren't accessing the JSON of the response correctly. You need to call the Body#json method of the response to actually access the JSON content. Also note that it returns a promise that resolves to the JSON content of a response:
fetch('…', { … })
.then(res => res.json())
.then(json => {
console.log(json.msg);
});
Also, I recommend sending an HTTP status code along with the JSON instead of having an ok property. That is a standard way and has builtin support through the Response object:
res.status(200).json({ … });

Related

How to pass the response array from node in the catch block in a fetch promise in React

I am working on a React and Node app and I don't understand how to pass the error given from the back end to the catch block in the fetch in the front end.
The login function uses fetch that throws an error if the server returns a not-ok status. The server also returns an array of errors that I need to display on the front end.
My problem is that when forcing an error and throwing the error to be caught in the catch block of the fetch promise, I cannot feed the catch with the array of errors returned by the back end.
I feed the response to the catch and there when it is logged it says it is an object Response. And it does not have the errors property coming from the back end response.
This is the login function on the front end:
function handleLogin() {
fetch('http://localhost:5000/auth/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
})
.then((response) => {
if(!response.ok) {
throw Error(response)
}
return response.json()
})
.then((token) => {
localStorage.setItem('token', token);
history.push('/');
window.location.reload();
})
.catch((error) => {
console.log('error: ', error); // error: Error: [object Response]
console.log('error:', error.errors); // undefined
setErrors(error.errors)
})
}
This is the controller of the login in the back end:
exports.login = async (req, res) => {
const { password, username } = req.body;
const hasErrors = validationResult(req);
// VALIDATE INPUTS
if (!hasErrors.isEmpty()) {
console.log('there are errros')
return res.status(401).json({
erros: hasErrors.array(),
});
}
// VALIDATE USER
const user = await User.findOne({ username });
if (!user) {
return res.status(401).send({
erros: [
{
msg: 'Invalid Credentials 1',
},
],
});
}
const isValid = await bcrypt.compare(password, user.password);
if (isValid) {
// SIGN THE JWT
const token = await JWT.sign({ username }, 'mysecret', {
expiresIn: 864_000,
});
return res.json(token);
} else {
return res.status(401).send({
erros: [
{
msg: 'Could not save the user into the db',
},
],
});
}
}

How to estabilish express session with passport.js trough multiple APIs

nodejs newbie here.
Currently i'm working on implementing local authentication via passport.js for admin user. I have the following setup: web client for admin, api for admin, and service api.
So the call for auth is goes like this:
clientAdmin > apiAdmin > serviceApi (auth) > apiAdmin > clientAdmin.
However, express-session is not working properly on this setup, its just doesen't store passport in session and never runs deserializeUser function. I assume that when serviceApi responds to apiAdmin, its trying to store a cookie in it, but apiAdmin can't store it.
What shall i do to get the cookie from response on apiAdmin side, and then pass it to the client? Or maybe i can kinda resend the response that i get from serviceApi to client?
P.S. If i do a call directly from client to service, ignoring adminApi, all works fine, so my middleware and passport config are configured properly. But i need to implement a setup with 2 apis.
Function on client that calls adminapi
const login = () => {
axios({
method: "POST",
data: {
username: username,
password: password,
},
withCredentials: true,
url: config.loginURL,
}).then((res) => {
if (res.data.userId) {
setUser({ id: res.data.userId });
}
});
};
Function on adminapi that calls serviceapi
loginHandler(req: express.Request, res: any) {
axios({
method: "POST",
data: req.body,
withCredentials: true,
url: config.loginURL,
})
.then((response) => {
res.send(response.data);
})
.catch((err) => console.error(err));
}
Function on serviceapi
loginHandler(req: any, res: any, next: express.NextFunction) {
passport.authenticate("local", (err, user) => {
if (err) throw err;
if (!user) res.json({ message: "Invalid credentials" });
else {
req.logIn(user, (err: Error) => {
if (err) throw err;
res.json({ message: "Authorized", userId: user.id });
});
}
})(req, res, next);
}

Unable to get cookie authentication working with pouchdb

I'm having trouble getting pouchdb working on the client side. I've looked through several other Stack Overflow posts, but I still haven't been able to figure it out. I've also looked into pouchdb-authentication, but it doesn't seem to be the one I'm looking for.
Below is my Express backend.
I first get the password from the user and validate it before making a post request to /_sessions to get the token. I then return a string back to the client. The string looks like this:
AuthSession=dGVzdDo1RTRDMDlFNDoCqn-ogpyksRGBXe7x7DVinw075A; Version=1;
Expires=Tue, 18-Feb-2020 16:09:32 GMT; Max-Age=600; Path=/; HttpOnly
In the POST route for localhost:8080/session
bcrypt.compare(req.body.password, users[0].password, function (error, result) {
if (error) { res.status(400).json({ error }); }
if (result === true) {
// return a cookie auth token from couchdb
axios.post('http://localhost:5984/_session', {
name: req.body.username,
password: users[0].password
}).then((response) => {
const cToken = response.headers['set-cookie'][0];
return res.json(cToken); // send auth token back to client
}).catch((err) => {
return res.status(400).json(err);
});
});
};
However, the client side doesn't seem to recognize that I got a cookie token and I get this error:
{ error: "unauthorized", reason: "You are not authorized to access this
db.", name: "unauthorized", status: 401, message: "You are not
authorized to access this db." }
Here is my client side code:
axios.post('http://localhost:8080/session/', { username: this.form.username, password: this.form.password }).then((res) => {
console.log(res.data);
const db = new PouchDB('http://localhost:5984/somedb', { headers: { Cookie: res.data } });
db.info().then((info) => {
db.put({
_id: 'mydoc',
title: 'test'
}).then((response) => {
// handle response
}).catch((err) => {
console.log(err);
});
});
}).catch((error) => {
console.log(error);
});
Is it because I'm not getting the header/the cookie isn't being set? Can I not just pass the string when I create the pouchdb instance?
Any help or guidance would be appreciated. Thanks in advance.

I cannot send data to the server and i can't and I also did not receive data from the server

I am learning react-native and I have some problems developing the login function
this is my fetch function in react-native
getLogin = () => {
fetch("http://localhost:5000/auth/login", {
method: 'POST',
header: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: `${this.state.email}`,
pass: `${this.state.pass}`,
}),
})
.then((res) => res.json())
.then((data) => {
console.log(this.state)
console.log(data)
if (!this.state.success) {
console.log(data)
console.log("Login false!");
}
else {
console.log(data);
console.log("Login success");
}
})
.catch((error) => {
console.error(error);
});
}
this is my code in server
module.exports.postLogin = (req, res) => {
console.log(req)
var email = req.body.email
var pass = req.body.pass
var user = db.get('users').find({ email: email }).value()
if (!user) {
res.json({
errors: [
"user does not exist "
],
values: req.body,
})
}
if (user.pass !== pass) {
res.json({
errors: [
"wrong pass "
],
values: req.body,
})
}
res.json({
success: true
})
res.cookie("userID", user.id, {
signed: true
})
}
I cannot send data to the server and i can't and I also did not receive data from the server
please help me, thanks for sp
You have two solutions :
Use the IP address of your machine instead of localhost and make sure that your machine and your device and emulator running in the same network
Solution2 : you can use ngrok https://ngrok.com/ that is will generate an HTTPS URL for your server code running in the port:5000 and it can be accessed from other devices
If you are running the app on a real device or android emulator, than your problem is that localhost is not pointing to your machine in which your server runs. See this answer on an issue.
The solution is to replace localhost with the IP address of your machine.

How to get a backend error message on the frontend using Fetch

I don't understand how fetch works for returning error messages from the backend to the frontend. I am not exactly sure what kind of properties to add to the error code in the fetch function on the frontend side. The goal is to send the error ("The email address is..") and display it in the UI.
Backend post request detail. If the user is already registered, throw a 400 error.
const signupPost = function (req, res) {
Base.findOne({
userName: req.body.userName
},
function (user, err) {
if (err) {
return res.status(400).send({
error: 'The email address you have entered is already associated with another account.'
});
}
Here's the method in my Vue frontend with the error handling:
methods: {
signup() {
this.errorMessage = '';
if (this.validUser()) {
const body = {
userName: this.user.userName,
firstName: this.user.firstName,
lastName: this.user.lastName,
telephone: this.user.telephone,
password: this.user.password
}
fetch(SIGNUP_URL, {
method: 'POST',
body: JSON.stringify(body),
headers: {
'content-type': 'application/json'
}
})
.then((response) => {
if (response.ok) {
return response.json();
}
return response.json().then((error) => {
throw new Error(error);
})
})
.then(user => {
console.log(user)
})
.catch((error) => {
this.errorMessage = error
});
}
},
You are almost there.
You should access error inside your returned body:
return response.json().then((body) => {
throw new Error(body.error)
})
And then use message on the error object returned:
.catch((error) => {
this.errorMessage = error.message
})

Resources