Two API call in a row in Vue.js - javascript

I have an app in Vue.js where user can post a review on a selected restaurant. To "POST" a review I have two functions linked to on #click event:
<button
class="btn btn-sm btn-primary mt-2 mr-2"
#click="addRestaurant(), addReview()"
>Add a Review</button>
Here is my two functions:
addRestaurant () {
let endpoint = `/api/restaurant/`;
let method = "POST";
apiService(endpoint, method, { maps: this.$route.params.maps, adress: this.$route.params.adress, name: this.$route.params.name })
},
addReview () {
let endpoint = `/api/restaurant_review/`;
let method = "POST";
apiService(endpoint, method, { maps: this.$route.params.maps, review_author: 1 })
},
It does work most of the time but I am facing two problems:
-If a restaurant instance allready exists, it throws a HTTP 400 error in my console. I've tried to catch it but with no success.
-It seems that sometimes addReview() is execued before addRestaurant() and I get an error because
this.$route.params.maps has a ForeignKey constraint.
So I tried to make only one function, with my two functions inside, trying to make addReview() executing as soon as addRestaurant() is done but I couldn't find a way. I also tried to make an if statement to check if my restaurant instance existed but I don't know if making so much API call in a row is good practice.
Any help is welcome I you think about the right solution to handle my problem
This my API service:
function handleResponse(response) {
if (response.status === 204) {
return '';
} else if (response.status === 404) {
return null;
} else if (response.status === 400) {
console.log("Restaurant allready added!")
return null;
} else {
return response.json();
}
}
function apiService(endpoint, method, data) {
const config = {
method: method || "GET",
body: data !== undefined ? JSON.stringify(data) : null,
headers: {
'content-type': 'application/json',
'X-CSRFTOKEN': CSRF_TOKEN
}
};
return fetch(endpoint, config)
.then(handleResponse)
.catch(error => console.log(error))
}
export { apiService };

First of all, in your handleResponse you can throw an error if something is wrong. That way, the error will end up in the catch method of the fetch call:
function handleResponse(response) {
if (response.status === 204) {
throw new Error("No data")
} else if (response.status === 404) {
throw new Error("Wrong URL")
} else if (response.status === 400) {
throw new Error("Restaurant already added!")
} else {
return response.json();
}
}
If handleresponse goes well, the fetch call will return a promise, but you're not handling that promise when you call apiService. I think it should look something like this:
addRestaurant () {
let endpoint = `/api/restaurant/`;
let method = "POST";
let config = { maps: this.$route.params.maps, adress: this.$route.params.adress, name: this.$route.params.name };
apiService(endpoint, method, config)
.then(data => {
console.log("restaurant added!" + data);
// now you can call the add review function
addReview();
})
.catch(error => console.log(error));
},

Related

axios: how to access response when getting 403 exception? [duplicate]

This may seem stupid, but I'm trying to get the error data when a request fails in Axios.
axios
.get('foo.example')
.then((response) => {})
.catch((error) => {
console.log(error); //Logs a string: Error: Request failed with status code 404
});
Instead of the string, is it possible to get an object with perhaps the status code and content? For example:
Object = {status: 404, reason: 'Not found', body: '404 Not found'}
What you see is the string returned by the toString method of the error object. (error is not a string.)
If a response has been received from the server, the error object will contain the response property:
axios.get('/foo')
.catch(function (error) {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
}
});
With TypeScript, it is easy to find what you want with the right type.
This makes everything easier because you can get all the properties of the type with autocomplete, so you can know the proper structure of your response and error.
import { AxiosResponse, AxiosError } from 'axios'
axios.get('foo.example')
.then((response: AxiosResponse) => {
// Handle response
})
.catch((reason: AxiosError) => {
if (reason.response!.status === 400) {
// Handle 400
} else {
// Handle else
}
console.log(reason.message)
})
Also, you can pass a parameter to both types to tell what are you expecting inside response.data like so:
import { AxiosResponse, AxiosError } from 'axios'
axios.get('foo.example')
.then((response: AxiosResponse<{user:{name:string}}>) => {
// Handle response
})
.catch((reason: AxiosError<{additionalInfo:string}>) => {
if (reason.response!.status === 400) {
// Handle 400
} else {
// Handle else
}
console.log(reason.message)
})
As #Nick said, the results you see when you console.log a JavaScript Error object depend on the exact implementation of console.log, which varies and (imo) makes checking errors incredibly annoying.
If you'd like to see the full Error object and all the information it carries bypassing the toString() method, you could just use JSON.stringify:
axios.get('/foo')
.catch(function (error) {
console.log(JSON.stringify(error))
});
There is a new option called validateStatus in request config. You can use it to specify to not throw exceptions if status < 100 or status > 300 (default behavior). Example:
const {status} = axios.get('foo.example', {validateStatus: () => true})
You can use the spread operator (...) to force it into a new object like this:
axios.get('foo.example')
.then((response) => {})
.catch((error) => {
console.log({...error})
})
Be aware: this will not be an instance of Error.
I am using this interceptors to get the error response.
const HttpClient = axios.create({
baseURL: env.baseUrl,
});
HttpClient.interceptors.response.use((response) => {
return response;
}, (error) => {
return Promise.resolve({ error });
});
In order to get the http status code returned from the server, you can add validateStatus: status => true to axios options:
axios({
method: 'POST',
url: 'http://localhost:3001/users/login',
data: { username, password },
validateStatus: () => true
}).then(res => {
console.log(res.status);
});
This way, every http response resolves the promise returned from axios.
https://github.com/axios/axios#handling-errors
Whole error can only be shown using error.response like that :
axios.get('url').catch((error) => {
if (error.response) {
console.log(error.response);
}
});
const handleSubmit = (e) => {
e.preventDefault();
// console.log(name);
setLoading(true);
createCategory({ name }, user.token)
.then((res) => {
// console.log("res",res);
setLoading(false);
setName("");
toast.success(`"${res.data.name}" is created`);
loadCategories();
})
.catch((err) => {
console.log(err);
setLoading(false);
if (err.response.status === 400) toast.error(err.response.data);//explained in GD
});
};
See the console log then you will understand clearly
With Axios
post('/stores', body).then((res) => {
notifyInfo("Store Created Successfully")
GetStore()
}).catch(function (error) {
if (error.status === 409) {
notifyError("Duplicate Location ID, Please Add another one")
} else {
notifyError(error.data.detail)
}
})
It's indeed pretty weird that fetching only error does not return an object. While returning error.response gives you access to most feedback stuff you need.
I ended up using this:
axios.get(...).catch( error => { return Promise.reject(error.response.data.error); });
Which gives strictly the stuff I need: status code (404) and the text-message of the error.
Axios. get('foo.example')
.then((response) => {})
.catch((error) => {
if(error. response){
console.log(error. response. data)
console.log(error. response. status);
}
})
This is a known bug, try to use "axios": "0.13.1"
https://github.com/mzabriskie/axios/issues/378
I had the same problem so I ended up using "axios": "0.12.0". It works fine for me.
You can put the error into an object and log the object, like this:
axios.get('foo.example')
.then((response) => {})
.catch((error) => {
console.log({error}) // this will log an empty object with an error property
});
It's my code: Work for me
var jsonData = request.body;
var jsonParsed = JSON.parse(JSON.stringify(jsonData));
// message_body = {
// "phone": "5511995001920",
// "body": "WhatsApp API on chat-api.com works good"
// }
axios.post(whatsapp_url, jsonParsed,validateStatus = true)
.then((res) => {
// console.log(`statusCode: ${res.statusCode}`)
console.log(res.data)
console.log(res.status);
// var jsonData = res.body;
// var jsonParsed = JSON.parse(JSON.stringify(jsonData));
response.json("ok")
})
.catch((error) => {
console.error(error)
response.json("error")
})

Using Promise.reject() for Error-Handling in http.post

I wrote a login method which sends a http post to an authorization service. If the credentials are wrong, the service returns a 403.
My idea is to return a Promise.reject() and let the caller catch the rejection to show an alert. My problem is, the calling method does not handle the error correctly.
Is there anything I am doing wrong with my error procedure?
public async login(username: string, password: string): Promise<void> {
let authToken = null;
const obj = this;
const url = this.backendService.getAuthServiceURL(username, password);
this.http.post<any>(url, { }, {
headers: new HttpHeaders(),
observe: 'response',
}).subscribe(
(result) => {
authToken = result.headers.get('Authorization');
localStorage.setItem(authTokenIdentifier, authToken);
obj.username = username;
localStorage.setItem(usernameIdentifier, username);
},
(error) => {
if (error.status === 403) {
localStorage.removeItem(authTokenIdentifier);
return Promise.reject(error.message);
} else {
return Promise.reject(error.message);
}
},
() => {
},
);
}
async login(): Promise<void> {
this.authService.login(this.username, this.password)
.then( () => console.log("SUCCESS!"))
.catch(() => console.log("ERROR!"));
}
I already tried wrapping the authService.login call in Try/Catch, calling it with await
await this.authService.login(this.username, this.password)
and not running the code async at all.
Thank you for any advice in advance.
Edit:
If I understand you guys correctly, returning the Error wrapped in a Promise is unnecessary and/or can cause problems. I have to admit I didn't really understand that subscribe will create an Observable.
(error) => {
if (error.status === 403) {
localStorage.removeItem(authTokenIdentifier);
throwError(error.message);
} else {
throwError(error.message);
}
},
I tried throwing the error directly with the same result.

How to let function continue if a return is empty

I'm working on a chat application and right now i'm working on the ability to create a new chat. To prevent the user to be able to create duplicate chats, i came up with this.
user1 is pre-defined. user2 is defined by entering it into a form. handleSubmit checks if this already exist and if "isChat" returns an id from my database the user gets redirected to the already existing chat.
I have trouble with the other part. If "isChat" is undefined my function wont continue and stops at the first await function.
async function handleSubmit(event) {
event.preventDefault();
const isChat = await getChatId(user1, user2);
if (isChat) {
setChatId(isChat);
setDefinedPartnerName(true);
} else {
await initiateNewChat(user1, user2, messages);
const chatId = await getChatId(user1, user2);
setChatId(chatId);
setDefinedPartnerName(true);
}
}
This is my fetch for this:
//Get chat ID by user1 and user2
export async function getChatId(user1, user2) {
return fetch(`/api/${user1}/${user2}`, {
method: 'GET'
})
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText);
}
return response;
})
.then(response => response.json());
}
This function cant return anything if there are no records for these two users.
Maybe the error fell through "throw"?
try:
export async function getChatId(user1, user2) {
return fetch(`/api/${user1}/${user2}`, {
method: 'GET'
})
.then(response => {
if (response.status !== 200) {
// throw new Error(response.statusText);
return false;
}else{
return response;
}
})
.then(response => response.json());
}
The syntax for error handling can also be improved:
https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
Solved it on my express route.
My express route has a try...catch function and i gave the catch part of it a response.json(false);
It is a hotfix. But I really don't want to spend a lot of time on backend stuff.
Thanks for your help everyone

function in catch block immediately invoked

I'm trying to raise an errorBoundary if an API call does not return a 200 but the raiseErrorBoundary is invoked immediately in catch block when the API call is called, how can I make it so it is invoked only when an error has been thrown?
export const downgradeAPICall = (raiseErrorBoundary) => {
return fetch('http://localhost:42005/Upgrade/Downgrade', {
method: 'POST'
}).then(res => {
if(res.status != 200){
throw new Error("An error has occured during API request!")
} else {
window.location.reload()
}
})
.catch((error)=>{
console.log(error)
raiseErrorBoundary()
})
}
200 is an "OK" status response. Right now you throw an error if it's successful so it will probably throw an error no matter what the way you've written it. Instead you probably want if(res.status !== 200)
(also "occurred" was misspelled as "occured")
export const downgradeAPICall = (raiseErrorBoundary) => {
return fetch('http://localhost:42005/Upgrade/Downgrade', {
method: 'POST'
}).then(res => {
if(res.status !== 200){
throw new Error("An error has occurred during API request!")
} else {
window.location.reload()
}
})
.catch((error)=>{
console.log(error)
raiseErrorBoundary()
})
}

Can I throw error in axios post based on response status

Is it possible to throw an error on purpose inside the .then() block in axios? For instance, if the api responds with 204 status code, could I throw an error and run the catch block?
For example:
axios.post('link-to-my-post-service', {
json-input
}).then(response => {
if (response.status === 200) {
//proceed...
}
else {
// throw error and go to catch block
}
}).catch(error => {
//run this code always when status!==200
});
EDIT
I tried this, but it didn't work:
var instance = axios.create({
validateStatus: function (status)
{
return status == 200;
}
});
axios.post('link-to-my-post-service', {input: myInput}, instance)
.then(response => {
dispatch({
type: "FETCH_SUCCESS",
payload: response.data
});
}).catch(error => {
dispatch({
type: "FETCH_FAILED",
payload: error
});
});
When I get a status code 204, still the executed block is then() block instead of the catch block.
EDIT 2
The correct answer using Ilario's suggestion is this:
var instance = axios.create({
validateStatus: function (status)
{
return status == 200;
}
});
instance.post('link-to-my-post-service', {input: myInput})
.then(response => {
dispatch({
type: "FETCH_SUCCESS",
payload: response.data
});
}).catch(error => {
dispatch({
type: "FETCH_FAILED",
payload: error
});
});
Now when the status code is not equal to 200 the catch block code is executed.
If you give a look at the GitHub Project Page you will notice following option description.
/* `validateStatus` defines whether to resolve or reject the promise for a given
* HTTP response status code. If `validateStatus` returns `true` (or is set to `null`
* or `undefined`), the promise will be resolved; otherwise, the promise will be
*/ rejected.
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
So you could create an Instance with your own configuration.
var instance = axios.create({
validateStatus: function (status) {
return status == 200;
},
});
You could also set defaults. These will be applied to every request.
axios.defaults.validateStatus = () => {
return status == 200;
};
UPDATE 1
To set the config only on a specific operation you could replace "config" with your desired values or methods.
axios.post(url[, data[, config]])
UPDATE 2
I tried this, but it didn't work.
You cannot pass the instance to axios.post(). You must call post on the new instance.
var instance = axios.create({
validateStatus: function (status) {
return status == 200;
}
});
instance.post('url', data, config);
Thank you very much for your suggestions. The answer was simpler than I expected.
I didn't want to set any default options to change the behavior of axios, so I just tried something like the code below, and it worked. Every time the code throw new Error("Error"); is executed, the catch block code is executed after that.
axios.post('link-to-my-post-service', {
json-input
}).then(response => {
if (response.status === 200) {
//proceed...
}
else {
// throw error and go to catch block
throw new Error("Error");
}
}).catch(error => {
//when throw "Error" is executed it runs the catch block code
console.log(error)
});

Categories