Nested Fetch with Async [duplicate] - javascript

This question already has answers here:
loging the response.json() inside the then() method before returning the response.json() promise
(1 answer)
JavaScript fetch - Failed to execute 'json' on 'Response': body stream is locked
(11 answers)
Closed 6 months ago.
I am trying to figure out what change I need to make to my flow where I am triggering a post call and on success return to run another fetch call followed by a 3rd party generated redirect (Stripe). At the moment I am receiving an error message at const session = await response.json()
with the message:
Failed to execute 'json' on 'Response': body stream already read
and I'm not sure how to refactor my code to be able to handle this issue. Can anyone point to what is wrong with my current setup and the rules I might be breaking?
const signUp = (e) => {
e.preventDefault();
if (password === passwordConfirmation) {
axios
.post(
"/api/auth/signup/",
{ email, password, passwordConfirmation },
{
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
}
)
.then((res) => {
const data = res.data;
setUser(data.user);
// Set the error message
if (data.message.status === "error") {
console.log("Error present");
setMessage(data.message);
}
return data.message.status;
})
.then(async (serverMessage) => {
// On successful sigin, redirect to /api/stripe/checkout-session/
if (serverMessage !== "error") {
// Get Stripe.js instance
const stripe = await stripePromise;
const response = await fetch("/api/stripe/checkout-session/", {
method: "POST",
});
console.log(`response: ${response.json()}`);
const session = await response.json();
console.log(`session: ${session}`);
// When the customer clicks on the button, redirect them to Checkout.
const result = await stripe.redirectToCheckout({
sessionId: session.sessionId,
});
// router.push('/api/stripe/checkout-session/')
// router.push('/app/feed/')
}
})
.catch((err) => {
console.log(err);
console.log(err.request);
console.log(err.message);
});
} else {
setMessage({
status: "error",
body: "Passwords do not match. Please try again.",
});
}
};

Do not call .json() twice.
change
console.log(`response: ${response.json()}`);
const session = await response.json();
to
const session = await response.json();
console.log(`response: ${session}`);

you can only read body stream once :) Try this
const signUp = (e) => {
e.preventDefault();
if(password === passwordConfirmation){
axios.post('/api/auth/signup/', { email, password, passwordConfirmation }, {
headers: {
'Content-Type': 'application/json'
},
withCredentials: true
}).then((res) => {
const data = res.data;
setUser(data.user)
// Set the error message
if(data.message.status === 'error'){
console.log('Error present')
setMessage(data.message)
}
return data.message.status
}).then(async (serverMessage) => {
// On successful sigin, redirect to /api/stripe/checkout-session/
if (serverMessage !== 'error'){
// Get Stripe.js instance
const stripe = await stripePromise;
const response = await fetch('/api/stripe/checkout-session/', { method: 'POST' });
const responseBody = await response.json()
console.log(`response: ${responseBody}`);
const session = responseBody;
console.log(`session: ${session}`);
// When the customer clicks on the button, redirect them to Checkout.
const result = await stripe.redirectToCheckout({
sessionId: session.sessionId,
});
// router.push('/api/stripe/checkout-session/')
// router.push('/app/feed/')
}
}).catch((err) => {
console.log(err)
console.log(err.request)
console.log(err.message)
})
} else {
setMessage({
'status': 'error',
'body': 'Passwords do not match. Please try again.'
})
}
}

Related

fetching post request don't log success if the request succeed

I'm trying to sign up new user, when I'm sending the post request the server register the user well, and I can see them in my data base, but I can't see the success log in my console (I can catch the error and it logs in my console).
Server side code:
var express = require("express");
const { Error } = require("mongoose");
const passport = require("passport");
var router = express.Router();
const User = require("../models/user");
const catchAsync = require("../utils/catchAsync");
router.post(
"/register",
catchAsync(async (req, res) => {
try {
const { email, username, password } = req.body;
const user = new User({ email, username });
await User.register(user, password);
} catch (e) {
throw new Error("Error signing up");
}
})
);
module.exports = router;
Client side code:
const sumbitHandler = async (data) => {
const { username, email, password } = data;
try {
await fetch("http://localhost:9000/users/register", {
method: "POST",
body: JSON.stringify({
username,
email,
password,
}),
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
if (res && !res.ok) {
throw new Error("ERROR");
}
console.log("Success");
})
.catch((e) => {
console.log(e.message);
});
} catch (e) {
console.log(e.message);
}
};
You are mixing the async/await style and the older .then() Promise-style. Choose one or the other (I strongly recommend async/await)
You are not transforming fetch's response into JSON, leaving it in Promise state.
Your server is never responding to the client! You need to add res.end(), res.send(), res.json() or something.
const sumbitHandler = async (data) => {
const { username, email, password } = data;
try {
const response = await fetch("http://localhost:9000/users/register", {...});
const serverResponse = await response.text(); // or response.json() if your servers sends JSON back
console.log("Success! serverResponse is = ", serverResponse ); // "Done!"
} catch (e) {
console.log(e.message);
}
};
Server :
...
await User.register(user, password);
res.send("Done!"); // or res.json({ status : "ok" }); etc.

Nodejs async loop function returns blank [duplicate]

I'm doing requests to my API server to authenticate a user, that's not the problem. The problem is that I don't know why my async function doesn't return anything, and I get an error because the data that I want from this function is undefined.
Don't worry if the error management is ugly and in general I can do this better, I'll do that after fixing this problem.
Utils.js class
async Auth(username, password) {
const body = {
username: username,
password: password
};
let req_uuid = '';
await this.setupUUID()
.then((uuid) => {
req_uuid = uuid;
})
.catch((e) => {
console.error(e);
});
let jwtData = {
"req_uuid": req_uuid,
"origin": "launcher",
"scope": "ec_auth"
};
console.log(req_uuid);
let jwtToken = jwt.sign(jwtData, 'lulz');
await fetch('http://api.myapi.cc/authenticate', {
method: 'POST',
headers: { "Content-Type": "application/json", "identify": jwtToken },
body: JSON.stringify(body),
})
.then((res) => {
// console.log(res);
// If the status is OK (200) get the json data of the response containing the token and return it
if (res.status == 200) {
res.json()
.then((data) => {
return Promise.resolve(data);
});
// If the response status is 401 return an error containing the error code and message
} else if (res.status == 401) {
res.json()
.then((data) => {
console.log(data.message);
});
throw ({ code: 401, msg: 'Wrong username or password' });
// If the response status is 400 (Bad Request) display unknown error message (this sould never happen)
} else if (res.status == 400) {
throw ({ code: 400, msg: 'Unknown error, contact support for help. \nError code: 400' });
}
})
// If there's an error with the fetch request itself then display a dialog box with the error message
.catch((error) => {
// If it's a "normal" error, so it has a code, don't put inside a new error object
if(error.code) {
return Promise.reject(error);
} else {
return Promise.reject({ code: 'critical', msg: error });
}
});
}
Main.js file
utils.Auth('user123', 'admin')
.then((res) => {
console.log(res); // undefined
});
Your Async function must return the last promise:
return fetch('http://api.myapi.cc/authenticate', ...);
or await the result and return it:
var x = await fetch('http://api.myapi.cc/authenticate', ...);
// do something with x and...
return x;
Notice that you don’t need to mix promise syntax (.then) with await. You can, but you don’t need to, and probably shouldn’t.
These two functions do exactly the same thing:
function a() {
return functionReturningPromise().then(function (result) {
return result + 1;
});
}
async function b() {
return (await functionReturningPromise()) + 1;
}
await is not to be used with then.
let data = await this.setupUUID();
or
let data=null;
setupUUID().then(res=> data = res)
I would try something like this:
const postReq = async (jwtToken) => {
const body = {
username: username,
password: password,
};
try {
const res = await fetch('http://api.myapi.cc/authenticate', {
method: 'POST',
headers: { "Content-Type": "application/json", "identify": jwtToken },
body: JSON.stringify(body),
})
if (res) {
if (res.status == 200) {
return res.json();
} else if (res.status == 401) {
const data = res.json();
console.log(data.message)
throw ({ code: 401, msg: 'Wrong username or password' });
} else if (res.status == 400) {
throw ({ code: 400, msg: 'Unknown error, contact support for help. \nError code: 400' });
}
}
} catch (err) {
console.error(err)
}
};
const Auth = async (username, password) => {
const jwtData = {
"origin": "launcher",
"scope": "ec_auth"
};
try {
const req_uuid = await this.setupUUID();
if (req_uuid) {
jwtData["req_uuid"] = req_uuid;
const jwtToken = jwt.sign(jwtData, 'lulz');
return await postReq(jwtToken);
}
} catch (err) {
console.error(err);
};
}

NextJS - Can you use a return from an api in a conditional?

I have a change password function that hits this api for verification and I want to display an error if the current password is incorrect.
Any direction on how to go about this or if what I'm doing makes no sense please point me in the right direction if would be so kind, it would be greatly appreciated!
case "PUT":
try {
const validContact = await Contact.findOne({ _id: req.body.id });
const valid = bcrypt.compareSync(
req.body.currentPassword,
validContact.password
);
if (valid) {
const hashedPassword = bcrypt.hashSync(
req.body.newPassword,
bcrypt.genSaltSync()
);
const contact = await Contact.findOneAndUpdate(
{ _id: req.body.id },
{ password: hashedPassword },
{ new: true }
);
res.status(200).json({ success: true, data: contact });
}
res.status(400).json({ success: false });
} catch (error) {
res.status(400).json({ success: false });
}
break;
This is the function that calls the API upon form submission
const submitNewPassword = (submitNewPasswordForm, resetForm) => {
submitNewPasswordForm(); // <-- I want to put this in a conditional
resetForm();
setOpenPasswordPopup(false);
setNotify({
isOpen: true,
message: "Password updated successfully",
type: "success",
});
};
edit: submitNewPassword function
const submitNewPasswordForm = async () => {
try {
const res = await fetch(`${process.env.APP_DOMAIN}/api/${apiRoute}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(values),
});
router.push(`${process.env.APP_DOMAIN}/`);
} catch (error) {
console.log(error);
}
};
Your submitNewPasswordForm doesn't return anything right now (well, it does, but it's just an empty Promise). To be able to check if it was a good request or not, you need to return something from it. Example:
const submitNewPasswordForm = async () => {
try {
const res = await fetch(`${process.env.APP_DOMAIN}/api/${apiRoute}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(values),
});
// this check is also necessary; 400 isn't an exception that would get caught
if (!res.ok) {
throw new Error(res.statusText)
}
router.push(`${process.env.APP_DOMAIN}/`);
return true;
} catch (error) {
console.log(error);
// you could also re-throw the error, or return something else
return false;
}
};
Because it's an async function, it returns a Promise, so you need to get the return value out with a .then:
const submitNewPassword = (submitNewPasswordForm, resetForm) => {
submitNewPasswordForm()
.then((ok) => {
if (!ok) {
// show error
} else {
resetForm();
setOpenPasswordPopup(false);
setNotify({
isOpen: true,
message: "Password updated successfully",
type: "success",
});
}
})
};
If you re-throw the error in the first function, you could .catch it rather than checking for an ok value. You could also make the second function an async function if you wanted to. Example:
const submitNewPassword = async (submitNewPasswordForm, resetForm) => {
try {
submitNewPasswordForm()
// rest of the code
} catch (err) {
// show error
}
}
Whichever way you go, you'll have to return something from the function in order to know if it was a success or not.
You will need to declare the submitNewPassword with async as you will require to await.
Then I added the await before the submitNewPasswordForm() and the happy and unhappy path.
You can check the result here.
const submitNewPassword = async (submitNewPasswordForm, resetForm) => {
const response = await submitNewPasswordForm(); // <-- I want to put this in a conditional
if(response.status === 200){
const body = await response.json()
//happy path
}else
{
const bodyError = await response.json()
//unhappy path
}
resetForm();
setOpenPasswordPopup(false);
setNotify({
isOpen: true,
message: "Password updated successfully",
type: "success",
});
};

How to run a Node script

I need to be able to run a node script to delete an object from an external API. So I should be able to run this command:
node server.js Customer55555
And it should delete the object.
I have called to the API by using Axios.
const axios = require("axios");
const API = "http://dummy.restapiexample.com/api/v1/employees";
function getAllEmployees() {
axios
.get("http://dummy.restapiexample.com/api/v1/employees")
.then(response => {
// console.log(response.data);
console.log(response.status);
function filterEmployee() {
const employeeData = response.data;
employeeData.filter(employee => {
console.log(employee);
});
// console.log(employeeData);
}
filterEmployee();
})
.catch(error => {
console.log(error);
});
}
function deleteEmployee() {
axios({
method: "DELETE",
url: "http://dummy.restapiexample.com/api/v1/delete/36720",
headers: { "Content-Type": "application/json" }
})
.then(
// Observe the data keyword this time. Very important
// payload is the request body
// Do something
console.log("user deleted")
)
.catch(function(error) {
// handle error
console.log(error);
});
}
// getAllEmployees();
deleteEmployee();
I am able to get an individual object, but I need to figure out how to delete it by running the command above.
You can do something like this:
const axios = require("axios")
const API = "http://dummy.restapiexample.com/api/v1/employees"
async function getAllEmployees(filter = null) {
try {
const response = await axios.get("http://dummy.restapiexample.com/api/v1/employees")
console.log(response.status)
let employeeData = response.data
if (filter) {
// return only employees whose name contains filter.name
employeeData = employeeData.filter(({ employee_name }) => {
return employee_name.toLowerCase().indexOf(filter.name.toLowerCase()) >= 0
})
}
return employeeData
} catch(error) {
console.error(error)
return []
}
}
async function deleteEmployee({ id }) {
if (!id) {
throw new Error('You should pass a parameter')
}
try {
const response = await axios({
method: "DELETE",
url: `http://dummy.restapiexample.com/api/v1/delete/${id}`,
headers: { "Content-Type": "application/json" }
})
console.log("user deleted " + id)
} catch(error) {
// handle error
console.error(error)
}
}
async function main(params) {
const employees = await getAllEmployees({ name: params[0] || '' })
// Returns a promise to wait all delete promises
return Promise.all(employess.map(employee => deleteEmployee(employee)))
}
// process.argv contains console parameters. (https://stackoverflow.com/questions/4351521/how-do-i-pass-command-line-arguments-to-a-node-js-program)
main(process.argv.slice(2)).then(() => {
// returns 0 (Success) (https://stackoverflow.com/questions/5266152/how-to-exit-in-node-js)
process.exit(0)
}).catch(() => {
// returns 1 (error)
process.exit(1)
})
You should adapt this sample to get proper filtering and error reporting.

“SyntaxError: Unexpected end of JSON input”

Trying to fetch data from API using async/await, but facing the above error after session expiration, how to handle that error and show user session expired or any other helpful error?
export const walletAddCardRequest = async (dispatch, token, payload, url) => {
try {
persist.store.dispatch(setLoader(true));
const headers = {Authorization: token}
const response = await api(url, "POST", payload, headers);
console.log("wallet add card ", response);
persist.store.dispatch(setLoader(false));
if (response.status === 200) {
const user = await response.json();
if (user) {
persist.store.dispatch(setLoader(false));
Alert.alert(
"",
"Your card added successfully."
);
navigateTo("wallet");
} else {
throw new Error("Error. Please try again");
}
} else {}
} catch (e) {
persist.store.dispatch(setLoader(false));
Alert.alert(e.message);
console.log("console:",e);
dispatch({
type: AUTH_ERROR
});
}
}

Categories