FormData POST request throws Network error - node.js

I have a react native app that allows users to upload images to the server along with additional data.
I'm using NodeJS and ExpressJS as my backend framework. The route for posting images:
router.post("/", checkAuth, upload.single("gallery_item_image"), function (req, res, next) {
Branch
.findById({ _id: req.body.branch_id })
.exec()
.then((doc) => {
if(!doc) {
return res.status(404).json({
message: "Invalid Branch ID",
})
}
galleryItem = new GalleryItem({
_id: mongoose.Types.ObjectId(),
branch_id: req.body.branch_id,
caption: req.body.caption,
description: req.body.description,
imageUrl: "https://someurl.com/" + req.file.filename,
imageId: req.file.id,
})
return galleryItem.save()
})
.then((response) => {
console.log(response);
res.status(201).json({
message: "Gallery item successfully added to the database",
galleryItem: {
_id: response._id,
branch_id: response.branch_id,
caption: response.caption,
description: response.description,
date: response.date,
imageUrl: response.imageUrl,
imageId: response.imageId,
meta: {
type: "GET",
url: "https://someurl.com/" + response._id,
}
}
})
})
.catch((error) => {
console.log(error);
res.status(500).json({
error: error
})
})
})
I'm using axios to make ajax calls and the function that is making these calls looks like this:
submit = async() => {
if(this.state.imagePicked && this.state.title.length > 0 && this.state.description.length > 0) {
this.setState({
isLoading: true
})
try {
console.log(`submit()----> getting user data from AsyncStorage...`)
let data = await AsyncStorage.getItem(CONSTANTS.ASYNC_STORAGE.USER)
data = JSON.parse(data)
console.log(`submit()----> user received from AsyncStorage: ${JSON.stringify(data)}`)
const formData = new FormData();
formData.append('gallery_item_image', this.state.imageData);
formData.append("branch_id", data.branchId)
formData.append("caption", this.state.title)
formData.append("description", this.state.description)
let apiResponse = await axios({
method: 'post',
url: CONSTANTS.API.GALLERY,
data: formData,
config: {
headers: {
'Content-Type': 'multipart/form-data'
}
}
})
let apiResponseJson = await apiResponse.json()
console.log(`submit()----> axios response: ${JSON.stringify(apiResponseJson)}`)
// const config = {
// method: 'POST',
// headers: {
// Authorization: data.authToken,
// 'Content-Type': 'multipart/form-data'
// },
// body: formData,
// };
// console.log(`submit()----> posting data to API: ${JSON.stringify(formData)}`)
// try {
// let response = await fetch(CONSTANTS.API.GALLERY, config)
// console.log(`submit()----> response from API: ${JSON.stringify(response)}`)
// this.setState({
// isLoading: false,
// isModalVisible: false
// })
// } catch(error) {
// console.log(`submit()----> error occured when posting data to API: ${error}`)
// this.setState({
// isLoading: false,
// error: true,
// errorMessage: `error occured when posting data to API: ${error}`
// })
// return;
// }
this.setState({
isLoading: false
})
} catch(error) {
console.log(`submit()----> error occured when getting data from AsyncStorage: ${error}`)
this.setState({
isLoading: false,
error: true,
errorMessage: error
})
return;
}
return;
} else {
console.log(`submit()----> empty field found`)
return;
}
return;
}
However, I always get "Network Error" without any explanation.
console output
Application output

Related

Why is req.body empty {} (GET REQUEST)

This is my Frontend code
const fetchData = () => {
const options = {
method: 'GET',
url: 'http://localhost:1337/user/chart',
headers: {'x-access-token': sessionStorage.getItem('token')},
body: [chartData.datasets]
}
axios.request(options).then((response) => {
console.log(response)
}).catch((error) => {
console.error(error)})
}
This is backend
app.get('/user/chart', async (req, res) => {
const token = req.headers['x-access-token']
if (!token){
return res.status(404).json({ success: false, msg: "Token not found" });
}
try {
const decoded = jwt.verify(token, process.env.access_secret)
const email = decoded.email
await User.updateOne(
{ email: email },
{ $set: {} },
)
console.log(req.body)
return res.status(200).json({message: 'ok', label:[]})
} catch (error) {
console.log(error)
res.json({ status: 'error', error: 'invalid token' })
}
})
When I console.log(req.body) it is an empty {}.
Why is it empty?
I am using a GET request to retrieve the chart data
Axios API does not accept body on get get request you can send parameters with params example
const url = '/user/chart';
const config = {
headers: {'x-access-token': sessionStorage.getItem('token')},
params:{someKey:chartData.datasets}
};
axios.get(url, config)
Axios doesn't support setting a body for a get request, see the docs or this related question.
Though, I'd also recommend to reconsider your design. Typically the body isn't used in a GET request. If you're sending data to the server, you likely want to use POST or PUT instead. If you just want to pass a parameter, then you likely want to use request parameters.
If you absolutely need to send a body in your GET request, then you'll need to use a different tool.
frondend //
const fetchData = () => {
const options = {
method: 'POST',
url: 'http://localhost:1337/user/chart',
headers: {'x-access-token': sessionStorage.getItem('token')},
body: {name : "xx",mail:"xx#"}
}
axios.request(options).then((response) => {
console.log(response)
}).catch((error) => {
console.error(error)})
}
backend //
app.post('/user/chart', async (req, res) => {
const {name , mail} = req.body
const token = req.headers['x-access-token']
if (!token){
return res.status(404).json({ success: false, msg: "Token not found" });
}
try {
const decoded = jwt.verify(token, process.env.access_secret)
const email = decoded.email
await User.updateOne(
{ email: email },
{ $set: {} },
)
console.log(req.body)
return res.status(200).json({message: 'ok', label:[]})
} catch (error) {
console.log(error)
res.json({ status: 'error', error: 'invalid token' })
}
})Ï

Error only shows at the back-end but not correctly at the front-end

I am trying to make sure that the front-end of my app will display the error that I want it to display. I am purposely trying to create a user that already exists and therefore show my custom User already exists. Please log in. error.
It shows in postman when sending a request to the same endpoint, but at the front-end, the response just shows the following and not the actual error message I defined:
Response {type: 'cors', url: 'http://localhost:8000/api/user/create', redirected: false, status: 409, ok: false, …}
body: (...)
bodyUsed: false
headers: Headers {}
ok: false
redirected: false
status: 409
statusText: "Conflict"
type: "cors"
url: "http://localhost:8000/api/user/create"
[[Prototype]]: Response
The createUser controller:
export const createUser = async (req: Request, res: Response) => {
const { email, password } = req.body;
const existingUser = await User.findByEmail(email);
if (existingUser) {
return res.status(409).send('User already exists. Please log in.');
}
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = new User(email, hashedPassword);
const saveToDb = await newUser.saveToDb();
if (!saveToDb) {
return res.status(500).send('Could not insert user into the database.');
}
const token = newUser.signToken();
res.status(201).json({ token, id: saveToDb.insertedId });
};
The front-end submission:
const onSubmit: SubmitHandler<CreateAccountFormInputs> = async (formData) => {
try {
setLoading(true);
const res = await fetch('http://localhost:8000/api/user/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
console.log(res); // output shown above
if (!res.ok) {
setError(`HTTP error: ${res.status}`);
return;
}
navigate('/login');
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
}
Inside the if (!res.ok) block, I want to set the error to the custom message that should be returned by the API. But I can't set it if it's not returned.
Does anyone know what I'm doing wrong here?

JWT Nodejs-Accessing payload data in another file using Decode

I have a controllerfile where I use passport.authenticate. I declare my payload and sign my token now i need the info declared in the payload in another file so I could use them in my sql request.
Here's the code for the login auth :
login: (req, res, next) => {
console.log(" login");
passport.authenticate("local", { session: false }, (error, user) => {
console.log("executing callback auth * from authenticate for local strategy ");
//if there was an error in the verify callback related to the user data query
if (error || !user) {
next(new error_types.Error404("Email ou Mot de passe invalide !"))
}else {
console.log("*** Token generation begins ***** ");
console.log(user)
const payload = {
sub: user.id,
exp: Date.now() + parseInt(process.env.JWT_LIFETIME),
email: user.email,
name: user.prenom,
lastName: user.nom,
type:user.type,
};
const token = jwt.sign(JSON.stringify(payload), process.env.JWT_SECRET, {algorithm: process.env.JWT_ALGORITHM});
res.json({ token: token,type: user.type,userid:user.id });//added userid
}
})(req, res);
}
Now in my other file i need to get the user.id and user.type so that i could use them in my request :
const createProp=(req, res, next) => {
let con=req.con
let { xx,yy } = req.body;
con.query('INSERT INTO tab1
(xx,yy,user_id,user_type ) VALUES ($1, $2, $3, $4) ',[xx,yy,user_id,user_type],
(err, results) => {
if (err) {
console.log(err);
res.status(404).json({error: err});
}
else
{res.status(200).send(`success`)}
}
);
}
in my frontend VUEJS this is my file:
import router from '#/router'
import { notification } from 'ant-design-vue'
import JwtDecode from "jwt-decode";
import apiClient from '#/services/axios'
import * as jwt from '#/services/jwt'
const handleFinish = (values) => {
const formData = new FormData()
for (var key of Object.keys(formState)) {
formData.append(key, formState[key])//here im appending some fields in my
//form i have more than just xx,yy files i just put them as an
//example
}
const token = localStorage.getItem("accessToken");
var decoded = JwtDecode(token);
console.log(decoded)
formData.append('user_id',decoded.sub)
formData.append('user_type',decoded.type)
fileListFaisabilite.value.forEach((file) => {
formData.append('xx', file)
})
fileListEvaluation.value.forEach((file) => {
formData.append('yy', file)
})
// store.dispatch('user/PROPOSITION', formData)
}
methods:{
PROPOSITION({ commit, dispatch, rootState }, formData ) {
commit('SET_STATE', {
loading: true,
})
const proposition=
mapAuthProviders[rootState.settings.authProvider].proposition
proposition(formData)
.then(success => {
if (success) {
notification.success({
message: "Succesful ",
description: " form submited!",
})
router.push('/Accueil')
commit('SET_STATE', {
loading: false,
})
}
if (!success) {
commit('SET_STATE', {
loading: false,
})
}
})
return apiClient
.post('/proposition', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then(response => {
if (response) {
return response.data
}
return false
})
.catch(err => console.log(err))
},
},
What im looking for is how i can store in my database the userid and usertype using insertinto sql request.
You can set user data in your jwt sign function without stringify method:
const payload = {
sub: user.id,
exp: Date.now() + parseInt(process.env.JWT_LIFETIME),
email: user.email,
name: user.prenom,
lastName: user.nom,
type: user.type // <-- Add this
};
const token = jwt.sign(
payload, // Don't use JSON.stringify
process.env.JWT_SECRET,
{algorithm: process.env.JWT_ALGORITHM}
);
And access user info:
jwt.verify(token, process.env.JWT_SECRET, (err, payload) => {
if (err) {
// Handle error
}
// Get some data
let user_id = payload.sub;
let user_type = payload.type;
console.log(user_id, user_type);
next();
});
The vue file:
PROP({ commit, dispatch, rootState }, payload ) {
commit('SET_STATE', {
loading: true,
});
const prop = mapAuthProviders[rootState.settings.authProvider].prop
prop(payload)
.then(success => {
if (success) {
// success contains user information and token:
const { token, userid, type } = success;
// Save to localStorage (Optional)
localStorage.setItem("accessToken", token);
localStorage.setItem("userid", userid);
localStorage.setItem("type", type);
// This not works if don't have a JWT SEED
// var decoded = JwtDecode(token);
commit('SET_STATE', {
user_id: userid,
user_type: type,
})
//dispatch('LOAD_CURRENT_ACCOUNT')
notification.success({
message: "Succesful ",
description: " form submited!",
})
router.push('/Home')
commit('SET_STATE', {
loading: false,
})
}
if (!success) {
commit('SET_STATE', {
loading: false,
})
}
})
},
The api call file:
export async function prop(payload) {
try {
const response = await apiClient.post('/prop', payload, {
headers: { 'Content-Type': 'multipart/form-data'},
});
if (response) {
return response.data;
}
} catch (err) {
console.log(err);
}
return false;
}

When upload photo, Error: can't set header after they are sent,

I Tried to upload photo through an api, this api isconsuming another api using axios but when i upload using this, it giving error:
Error: can't set header after they are sent,
when I debugged res.status after axios process throwing error to the catch block
Do I miss something, here's the code :
upload: async (req, res) => {
const { subscriberId } = req.query;
const file = req.file;
if (!file) {
return res
.status(httpStatus.forbidden)
.json({
message: 'No File Found'
});
}
try {
const form = new FormData();
// console.log(form);
form.append('file',
file.buffer,
file.originalname
);
await axios.post(`${config.get('UPLOAD_PHOTO')}?subscriberId=${subscriberId}`, form,
{
headers: {
'api-key': `${config.get('API_KEY_CORE_SERVICE')}`,
'Request-Id': req.requestId,
'Content-Type': `multipart/form-data; boundary=${form._boundary}`
}
})
.catch((error) => {
// console.log(error.response.data)
res.status(httpStatus.internalServerError).json({
success: false,
message: error.message,
code: errorCodes.internalServerError
});
});
return res.status(httpStatus.ok).json({
success: true,
status: httpStatus.ok,
message: 'Successfully Upload Photo',
});
} catch (e) {
return res.status(e.statusCode || httpStatus.internalServerError).json({
success: false,
message: (e.error && e.error.message) || e.message || e,
code: errorCodes.internalServerError
});
}
You can remove this
.catch((error) => {
// console.log(error.response.data)
res.status(httpStatus.internalServerError).json({
success: false,
message: error.message,
code: errorCodes.internalServerError
});
});
You already have a try catch in place that basically does the same when this rejects/throws
await axios.post(`${config.get('UPLOAD_PHOTO')}?subscriberId=${subscriberId}`, form,
{
headers: {
'api-key': `${config.get('API_KEY_CORE_SERVICE')}`,
'Request-Id': req.requestId,
'Content-Type': `multipart/form-data; boundary=${form._boundary}`
}
})

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