So, my problem is when I try to login in my vue app, the backend automatically stops when I try to fetch from it an array of objects.
To be more specific. This is my fetch "attempt" to retrieve the objects from the database.
let url = utils.url;
let requestParam = utils.globalRequestParameters;
requestParam.method = "GET";
requestParam.body = null;
if (cars.value.length == 0) {
fetch(url "cars", requestParam).then((res) =>
res.json().then(async (res) => {
store.dispatch("Car/fetchCars", res);
fetch(url "users", requestParam).then((users) =>
users.json().then((users) => {
for (let car of res) {
let userCar = Object.values(users).find(
(a) => a.id == car.userId
);
car.userName = userCar.lastName " " userCar.firstName;
}
})
);
})
);
}
And login in view Login.vue
let requestParameters = utils.globalRequestParameters;
requestParameters.method = "POST";
requestParameters.body = JSON.stringify(data);
fetch(utils.url "login", requestParameters).then((res) => {
res.json().then((res) => {
this.mesaj = res.message;
console.log("token:" res.token);
if (res.token) {
localStorage.setItem("token", res.token);
console.log("token:" res.token);
console.log("id:" res.id);
let id = res.id;
requestParameters.method = "GET";
requestParameters.body = null;
this.$store.dispatch("login", true);
fetch(utils.url "users/" id, requestParameters).then(
(res) => {
res.json().then((res) => {
console.log("Jos de tot");
this.$store.dispatch("User/setUser", res);
console.log(this.$store.state.User.user);
this.$router.push("/");
});
}
);
}
});
});
}
},
Note. cars is a computed value that return store.state.cars and utils is
let url = "http://127.0.0.1:3000/";
let globalRequestParameters = {
method: "GET",
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
redirect: "follow",
referrerPolicy: "no-referrer",
};
module.exports.globalRequestParameters = globalRequestParameters;
module.exports.url = url;
Here at the first fetch the backend stops and also the fetch it is not done.
And the backend route is
router.get('/cars', async (req, res) => {
res.json(await functions.getAllCars(req,res));
})
getAllCars = async (req, res) => {
const snapshot = await db.collection("Cars").get();
let cars = [];
snapshot.forEach((doc) => {
let car = {
id: doc.id,
userId: doc.data().userId,
manufacturer: doc.data().manufacturer,
model: doc.data().model,
color: doc.data().color,
plate: doc.data().plate,
price: doc.data().price,
description: doc.data().description
};
cars.push(car);
});
res.status(200).send(cars);
return
};
router.get("/users/:id", async (req, res) => {
res.json(await functions.getUserById(req.params.id, res));
});
getUserById = (id, res) => {
db
.collection("Users")
.doc(id)
.get()
.then((response) => {
let user = {};
user.id = response.id;
user.firstName = response.data().firstName;
user.lastName = response.data().lastName;
user.gender = response.data().gender;
user.jobTitle = response.data().jobTitle;
user.phone = response.data().phone;
user.email = response.data().email;
user.isAdmin = response.data().isAdmin;
res.status(200).send(user);
return
})
.catch((err) => {
res.status(404).send({ message: "User not found" });
return
});
};
The user is retrieved correctly, I see it in console through a console log, but the messages that I get in the terminal and console are:
*As a final note. I use vue 3, node.js version 16.13.0 and Firestore as Database. And yesterday everything was working perfectly fine on my other computer but I had to go somewhere and use my laptop. Maybe there is something about my laptop. All I did was just to install the modules for the front and back
CodePudding user response:
I think this has nothing to do with Vue - it is simply the problem of your Express backend code
ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client
As described here:
That particular error occurs whenever you try to send more than one response to the same request and is usually caused by improper asynchronous code.
getAllCars
getAllCars
is async function with await
inside - as soon as this await
is hit (together with db.collection("Cars").get()
call), function returns Promise which is awaited at res.json(await functions.getAllCars(req,res));
When the DB call finishes, the rest of the method is executed including res.status(200).send(cars)
- this will send the cars
array to the client and returns undefined
(this is what simple return
does) and res.json(undefined)
is executed causing the error above (you are trying to send second response)
getUserById
You say that this handler works fine but I really doubt it - from what I see, this should NOT work either
You are calling it with res.json(await functions.getUserById(req.params.id, res));
. To await
actually doing something, the awaited function must return a Promise (either implicitly by using await
inside or explicitly) or general "thenable" object. The getUserById
function returns nothing (return
statements inside then()
or catch()
does not count! ...those are different functions)
This problem can be fixed by doing return db.collection("Users").doc(id).get().then()
but then you will get same error as in getAllCars
case
Correct pattern
- Do not use
res.status(200).send()
andres.json()
together - For the sake of sanity (at least until you really know what you are doing) do not mix promises with
async
/await
async
functions should return the data (do not usereturn
without "argument")
Following code shows both Promise based and async
/await
style (it "pseudo code" in the sense I did not tested it but hopefully you get the idea)
router.get('/cars', async (req, res) => {
try {
const response = await functions.getAllCars()
res.status(200).json(response);
} catch() {
res.sendStatus(500)
}
})
getAllCars = async () => {
const snapshot = await db.collection("Cars").get();
let cars = [];
snapshot.forEach((doc) => {
let car = {
id: doc.id,
userId: doc.data().userId,
manufacturer: doc.data().manufacturer,
model: doc.data().model,
color: doc.data().color,
plate: doc.data().plate,
price: doc.data().price,
description: doc.data().description
};
cars.push(car);
});
// res.status(200).send(cars); //* should be handled by caller
return cars //* return the data
};
router.get("/users/:id", async (req, res) => {
functions.getUserById(req.params.id)
.then((response) => {
if(response === null)
res.status(404).json({ message: "User not found" });
else
res.status(200).json(response);
})
.catch(er) {
res.status(500).send(er.message)
}
});
getUserById = (id) => {
return db //* return the promise
.collection("Users")
.doc(id)
.get()
.then((response) => {
let user = {};
user.id = response.id;
user.firstName = response.data().firstName;
user.lastName = response.data().lastName;
user.gender = response.data().gender;
user.jobTitle = response.data().jobTitle;
user.phone = response.data().phone;
user.email = response.data().email;
user.isAdmin = response.data().isAdmin;
// res.status(200).send(user); //* should be handled by caller
return user //* return the data
})
.catch((err) => {
return null
});
};