This question already has answers here:
Trying to use fetch and pass in mode: no-cors
(9 answers)
Closed 19 days ago.
I was making a site for a personal project when I ran into an error.
The client requests for the list of directories and the server returns a JSON file back to the client but the client brings up the error 'syntaxerror: unexpected end of input'. I checked the server output and it says it sent the file successfully, and when I accessed the server through the web manually, I got a valid JSON output (I checked it online). Is this a problem with the client?
server code:
app.get('/votes', (req, res) => {
fs.readFile(path.resolve(__dirname, 'testing.json'), (err, data) => {
if (err) {
res.status(500).send('Error reading file');
} else {
res.status(200).json(JSON.parse(data));
}
});
});
client code:
async function dataFetch() {
await fetch('https://serverIP/votes', {
method: 'GET',
mode: "no-cors",
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
console.log(`${data} <-- data`);
jsonFile = data;
})
.catch(error => {
alert("an error has occured: "+error)
console.log(`Error: ${error}`);
}
I tried using other methods of fetching, but none were successful.
i think you missed the } at the end.
await fetch('https://serverIP/votes', {
method: 'GET',
mode: "no-cors",
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
console.log(`${data} <-- data`);
jsonFile = data;
})
.catch(error => {
alert("an error has occured: "+error)
console.log(`Error: ${error}`);
})}```
I am writing a simple post request in a Firebase Cloud function, with Axios. This function calls an API endpoint and gets an object of profile details as response. My problem is to correctly return the response to the client.
In the code below, the Cloud Function logs the result correctly. But I can't figure out how to correctly return it to the client from the client-side callGetProfile() function. (Which runs inside a Vue3 method.)
I am probably missing something obvious but am very new to Node.js and HTPP requests.
Thanks for any help!
// MY FUNCTION IN NODE.JS (Firebase Cloud Functions)
exports.getProfile = functions.https.onCall((data, context) => {
var postData = {
profile_id: "xxxxxxxxxxxxxxxxx", //hardcoded here for testing but should be passed in "data" arg.
profile_type: "personal",
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'X-API-KEY': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX'
}
};
axios.post('xxxxxxxxxxxxxxxxxxxxxxxxxxxxx', postData, axiosConfig)
.then((res) => {
console.log(res.data) // this works, I get all the data correctly!!
return res // also tried res.data
})
.catch((err) => {
console.log("AXIOS ERROR: ", err);
})
});
// MY FUNCTION CLIENT SIDE (Vue3 method)
const functions = getFunctions();
const callGetProfile() = httpsCallable(functions, "getProfile");
callGetProfile()
.then((result) => {
console.log(result.data) // this doesn't work, data is "null"
})
.catch((e) => console.log(e));
I am building a jwt token refresh logic (refresh the authentication token when it expires) with axios interceptors. The refresh part works well : axios intercepts the error, refreshes the token, and retries the request (and successfully gets an answer from the server).
However, the page that made the request that failed because of the expired token still catches the error. I feel like axios still returns the error to the function that made the call instead of just returning the retried request, but idk how.
Here is the code in my axios.js file :
import { boot } from "quasar/wrappers";
import axios from "axios";
import * as storage from "../helpers/storage";
import store from "../store/index.js";
import router from "../router/index.js";
const api = axios.create({
baseURL: process.env.API_URL,
crossdomain: true,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
api.interceptors.request.use(
function (config) {
if (config.url !== "/register") {
const accessToken = storage.getAccessToken();
if (accessToken) {
config.headers.Authorization = "Bearer " + accessToken;
}
}
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
api.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
if (error.response.data.message === "Expired JWT Token") {
const originalRequest = error.config;
api
.post("/token/refresh", { refresh_token: storage.getRefreshToken() })
.then(({ data }) => {
if (data !== undefined) {
storage.setTokens(data.token, data.refresh_token);
}
originalRequest.headers = { Authorization: `Bearer ${data.token}` };
return new Promise(() => {
axios.request(originalRequest).then((response) => {
return response;
});
});
})
.catch((error) => {
console.error(error);
});
} else if (error.response.data.message === "Invalid JWT Token") {
console.log("error");
store()
.dispatch("auth/logout")
.then(() => {
router().push({
name: "register-login",
query: { error: "invalid_token" },
});
router().go(0);
store().dispatch("setLoading", false);
});
} else {
return Promise.reject(error);
}
}
);
export default boot(({ app }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api
app.config.globalProperties.$axios = axios;
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
// so you won't necessarily have to import axios in each vue file
app.config.globalProperties.$api = api;
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
// so you can easily perform requests against your app's API
});
export { axios, api };
And here is an example of a request I do :
export function sendTags(context, payload) {
return new Promise((resolve, reject) => {
api
.post("/spot/addTags", payload)
.then(({ data }) => {
resolve(data);
})
.catch((error) => {
reject(error.response.data);
});
});
Any idea of what could be going wrong ?
You didn't return a success result in the error function of response interceptor.
api.interceptors.response.use(
function (response) {
return response;
},
function (error) {
if (error.response.data.message === "Expired JWT Token") {
// You didn't return here!
// change to:
return api.post()
.than(() => {
// resolve the final result here
return axios.request(originalRequest)
})
}
}
)
My client is Vue.js using a Vuex store. I am using passport.js for authentication on the server side. Login and account registration is working. Checking mongodb shows new data. But express is sending an undefined response to the client. This is my first major javascript project so I'm hoping it's something simple my eyes just can't see yet.
client: api.js
export async function registerUser(user) {
console.log("api to register user");
console.log(user);
const route = `${api}/register`;
return fetch(route, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(user)
})
.then(response => {
console.log(response.json());
return response.json();
})
.then(json => {
console.log(json);
return json;
})
.catch(err => {
console.error(err);
});
}
client: index.js (vuex store where res is undefined)
actions: {
async register(state, user) {
apis.registerUser(user).then(res => {
if (res.success) {
this.dispatch("loadUser");
alert("successfully registered");
}
});
},
async loadUser() {
apis.getUser().then(res => {
this.commit("setUser", res.user);
});
}
}
server: app.js
app.post('/api/v1/register', function(req, res) {
const success = true;
Users=new User({email: req.body.email, username : req.body.username});
console.log(req.body);
User.register(Users, req.body.password, function(err, user) {
if (err) {
console.log('account could not be saved');
success = false;
} else {
console.log('account saved');
}
})
res.send({success: success});
});
fetch error printing to console
The server console.logs in the app.js route indicate the req.body has the right data and user account is saved successfully. No errors occur on res.send but the client gets an undefined response.
After much banging my head against the table and some outside assistance, I found a solution. I had two main issues.
I was not preventing the default on the submit button so the form was refreshing before the request was properly handled.
Mishandling of javascript promises.
Working code below:
api.js
export function registerUser(user) {
console.log("api to register user");
console.log(user);
const route = `${api}/register`;
return fetch(route, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(user)
})
.then(response => {
console.log(response.json());
return response.json();
})
}
index.js (vuex store)
actions: {
register(state, user) {
apis.registerUser(user).then(res => {
if (res.success) {
alert("successfully registered");
}
}).catch(err => {
console.error(err);
});
}
}
app.js code remained the same
I am working on Reactjs redux on front-end and Rails API as a back-end.
So now I call API with Fetch API method but the problem is I cannot get readable error message like what I got inside the network tabs
this is my function
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
});
}
}
But when errors came I cannot figure out how to get readable response message like I got when I check on my browser network tabs
So this is what I got from the network tabs when I got errors.
My console
This is my rails code
def create
user = User.new(user_params)
if user.save
#UserMailer.account_activation(user).deliver_now
render json: user, status: 201
else
render json: { errors: user.errors }, status: 422
end
end
But I cannot find out how can I get that inside my function
Since the text is hidden inside promise within response object, it needs to be handled like a promise to see it.
fetch(bla)
.then(res => {
if(!res.ok) {
return res.text().then(text => { throw new Error(text) })
}
else {
return res.json();
}
})
.catch(err => {
console.log('caught it!',err);
});
Similar to your answer, but with a bit more explanation... I first check if the response is ok, and then generate the error from the response.text() only for the cases that we have a successful response. Thus, network errors (which are not ok) would still generate their own error without being converted to text. Then those errors are caught in the downstream catch.
Here is my solution - I pulled the core fetch function into a wrapper function:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Then when I use it, I define how to handle my response and errors as needed at that time:
fetchJSON(url, options)
.then((json) => {
// do things with the response, like setting state:
this.setState({ something: json })
})
.catch(error => {
// do things with the error, like logging them:
console.error(error)
})
even though this is a bit old question I'm going to chime in.
In the comments above there was this answer:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Sure, you can use it, but there is one important thing to bare in mind. If you return json from the rest api looking as {error: 'Something went wrong'}, the code return res.text().then(text => {throw new Error(text)}) displayed above will certainly work, but the res.text() actually returns the string. Yeah, you guessed it! Not only will the string contain the value but also the key merged together! This leaves you with nothing but to separate it somehow. Yuck!
Therefore, I propose a different solution.
fetch(`backend.com/login`, {
method: 'POST',
body: JSON.stringify({ email, password })
})
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
.then(response => { ...someAdditional code })
.catch(error => reject(error.message))
So let's break the code, the first then in particular.
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
If the response is okay (i.e. the server returns 2xx response), it returns another promise response.json() which is processed subsequently in the next then block.
Otherwise, I will AGAIN invoke response.json() method, but will also provide it with its own then block of code. There I will throw a new error. In this case, the response in the brackets throw new Error(response.error) is a standard javascript object and therefore I'll take the error from it.
As you can see, there is also the catch block of code at the very end, where you process the newly thrown error. (error.message <-- the error is an object consisting of many fields such as name or message. I am not using name in this particular instance. You are bound to have this knowledge anyway)
Tadaaa! Hope it helps!
I've been looking around this problem and has come across this post so thought that my answer would benefit someone in the future.
Have a lovely day!
Marek
If you came to this question while trying to find the issue because response.json() throws "Unexpected token at position..." and you can't find the issue with the JSON, then you can try this, basically getting the text and then parsing it
fetch(URL)
.then(async (response) => {
if (!response.ok) {
const text = await response.text()
throw new Error(text)
}
// Here first we convert the body to text
const text = await response.text()
// You can add a console.log(text), to see the response
// Return the JSON
return JSON.parse(text)
})
.catch((error) => console.log('Error:', error))
.then((response) => console.log(response))
I think you need to do something like this
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
return response.json();
})
.then(function(object){
if (object.errors) {
dispatch(update_errors(response));
throw new Error(object.errors);
} else {
console.log("succeed json re");
dispatch(update_user(json));
}
})
.catch(function(error){
this.setState({ error })
})
}
}
You can access the error message with this way:
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
})
// here's the way to access the error message
.catch(function(error) {
console.log(error.response.data.message)
})
;
The best choice is not to catch the error in the fetch because this will be useless:
Just in your api put a response with not code error
static GetInvoicesAllData = async (req,res) =>
{
try{
let pool = await new Connection().GetConnection()
let invoiceRepository = new InvoiceRepository(pool);
let result = await invoiceRepository.GetInvoicesAllData();
res.json(result.recordset);
}catch(error){
res.send(error);
}
}
Then you just catch the error like this to show the message in front end.
fetch(process.env.REACT_APP_NodeAPI+'/Invoices/AllData')
.then(respuesta=>respuesta.json())
.then((datosRespuesta)=>{
if(datosRespuesta.originalError== undefined)
{
this.setState({datosCargados:true, facturas:datosRespuesta})
}
else{ alert("Error: " + datosRespuesta.originalError.info.message ) }
})
With this you will get what you want.
You variables coming back are not in response.body or response.message.
You need to check for the errors attribute on the response object.
if(response.errors) {
console.error(response.errors)
}
Check here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
You should actually be returning an error response code from the server and use the .catch() function of the fetch API
First you need to call json method on your response.
An example:
fetch(`${API_URL}`, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userInfoParams)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => {
console.log("error", err)
});
Let me know the console log if it didn't work for you.