I am writing a mobile application with using React Native. At some part, I need to send a post request and get response including the error part. So, for some certain input, API(my own) returns 409 with a message. Example return:
{
"status": 409,
"message": "E-mail is already exists!"
}
Here, I want to take that message and show to the user. This is what I tried:
UserService.signup({ fullName, email, username, password })
.then(response => {
this.setState({ signUp: true });
if (response.result) {
Toast.show(messages.successfulSignUp, {
backgroundColor: "green",
duration: Toast.durations.LONG,
position: Toast.positions.TOP
});
this.props.navigation.navigate("SignIn");
} else {
}
})
.catch(error => {
Toast.show(error.message, {
backgroundColor: "red",
duration: Toast.durations.LONG,
position: Toast.positions.TOP
});
this.setState({ signUp: false });
});
I tried error.message, error.response, error, error.data keys, but it always says TypeError: undefined is not an object (evaluating 'error.message'). So, how can I get the message from error object?
Edit: This is how I send the request:
import { post } from "./api";
export default {
signup: ({ fullName, email, username, password }) => {
return post("/user/register", { fullName, email, username, password });
}
};
export const request = config => {
return new Promise((resolve, reject) => {
axiosInstance
.request({
url: config.url,
method: config.method || "get",
data: config.body,
headers: {
"Content-Type": "application/json",
"X-Auth-Token": store.getState().auth.token
}
})
.then(response => {
resolve(response.data);
})
.catch(error => {
reject(error.data);
});
});
};
export const post = (url, body = {}) => {
return request({
url,
body,
method: "post"
});
};
Finally I solved this issue. I had to change my request method and the way I reach out to the error:
export const request = (config) => {
return new Promise((resolve, reject) => {
axiosInstance.request({
url: config.url,
method: config.method || 'get',
data: config.body,
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': store.getState().auth.token,
}
}).then(response => {
resolve(response.data)
}).catch(error => {
reject(error.response)
})
})
}
// This is how reach out to the error message:
console.log(error.data.message);
Depending on what the backend returns, the error message in axios is in response.data of the error object.
.catch(error => {
const errResponse = (error && error.response && error.response.data)
|| (error && error.message);
reject(errResponse);
});
Related
On a vue application, on a component, calling API to get some data... the problem is that getting undefined, before the call ends... Possible needed aync/await but getting error when adding
//component code (login.vue)
import store from "#/store";
const { response, error } = store.postTO(url, [{id: "button1"}, {id: "button2"}, {id: "button3"}]);
if (response) {
console.log(response);
} else {
console.warn(error);
}
//store.js
export default {
user : null,
postTO
}
function postTO(url, postData) {
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: JSON.stringify(postData)
};
return fetch(url, requestOptions).then(response =>
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
console.log(res.data);
return {response : res.data, error : "test"};
}))
.catch(error => {
return {response : "test", error : error};
});
}
illustrated
In the component you have to wait for the fetch by putting an await before the store.posTo
I have already define error initially, but now I don't know why show me error like this
fetch data from backend code:
auth.js file
import fetch from "isomorphic-fetch";
import { API } from "../config";
export const signup = (user) => {
return fetch(`${API}/signup`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(user),
})
.then((res) => {
return res.json();
})
.catch((err) => {
console.log(err);
});
};
I have link connection inside API I'm sure is it ok.
After that when i submit my signup from then show me error, signup handleSubmit code:
signupComponent.js file
const handleSubmit = (e) => {
e.preventDefault();
//console.table({ name, email, password, error, loading, message, showForm });
setValues({ ...values, loading: true, error: false });
const user = { name, email, password };
signup(user).then((data) => {
try {
if (data.error) {
setValues({ ...values, error: data.error, loading: false });
} else {
setValues({
...values,
name: "",
email: "",
password: "",
error: "",
loading: false,
message: data.message,
showForm: false,
});
}
} catch (err) {
console.log(err);
}
});
};
I'm sure my internet connection is ok.
I don't know where is problem.
Any suggestion please.
auth.js file updated:
Return the err from catch
import fetch from "isomorphic-fetch";
import { API } from "../config";
export const signup = (user) => {
return fetch(`${API}/signup`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(user),
})
.then((res) => {
return res.json();
})
.catch((err) => {
return err;
});
};
You have a chain of callbacks
fetch().then().catch()
in your .then() you return the data but in your .catch() you return nothing.
Therefore your outer chain will try to process undefined incase the fetching failed.
signup().then(
(data) => ... data.error // data is undefined
)
I have solve my problem by this way:
used try catch block for find problem
In the catch block used .catch((err) => { return err; });
And make sure API connected succesfully
After that run my backend server
Then run my frontend.
I am a noob, using vue.js and a node auth api, the api works fine and provides the jwt token in the response, my question is how can i use the token in all the requests that follows (using axios), and any best practices for handling the token in the front end is also appreciated.
Thanks
You can use something like that for Your scenario in your vuejs app.
import axios from 'axios'
const API_URL = 'http://localhost:3000'
const securedAxiosInstance = axios.create({
baseURL: API_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
})
const plainAxiosInstance = axios.create({
baseURL: API_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
})
securedAxiosInstance.interceptors.request.use(config => {
const method = config.method.toUpperCase()
if (method !== 'OPTIONS' && method !== 'GET') {
config.headers = {
...config.headers,
'X-CSRF-TOKEN': localStorage.csrf
}
}
return config
})
securedAxiosInstance.interceptors.response.use(null, error => {
if (
error.response &&
error.response.config &&
error.response.status === 401
) {
return plainAxiosInstance
.post('/refresh', {}, { headers: { 'X-CSRF-TOKEN': localStorage.csrf } })
.then(response => {
localStorage.csrf = response.data.csrf
localStorage.signedIn = true
let retryConfig = error.response.config
retryConfig.headers['X-CSRF-TOKEN'] = localStorage.csrf
return plainAxiosInstance.request(retryConfig)
})
.catch(error => {
delete localStorage.csrf
delete localStorage.signedIn
location.replace('/')
return Promise.reject(error)
})
} else {
return Promise.reject(error)
}
})
export { securedAxiosInstance, plainAxiosInstance }
And in your component you use this to process your request with api
Products.vue
export default {
name: 'products',
data () {
return {
products: [],
newProduct: [],
error: '',
editedProduct: ''
}
},
created () {
if (!localStorage.signedIn) {
this.$router.replace('/')
} else {
this.$http.secured.get('/api/v1/products')
.then(response => { this.products = response.data })
.catch(error => this.setError(error, 'Something went wrong'))
}
},
methods: {
setError (error, text) {
this.error = (error.response && error.response.data && error.response.data.error) || text
},
addProduct () {
const value = this.newProduct
if (!value) {
return
}
this.$http.secured.post('/api/v1/products/', { product: { name: this.newProduct.name } })
.then(response => {
this.products.push(response.data)
this.newProduct = ''
})
.catch(error => this.setError(error, 'Cannot create product'))
},
removeProduct (product) {
this.$http.secured.delete(`/api/v1/products/${product.id}`)
.then(response => {
this.products.splice(this.products.indexOf(product), 1)
})
.catch(error => this.setError(error, 'Cannot delete product'))
},
editProduct (product) {
this.editedproduct = product
},
updateProduct (product) {
this.editedProduct = ''
this.$http.secured.patch(`/api/v1/products/${product.id}`, { product: { title: product.name } })
.catch(error => this.setError(error, 'Cannot update product'))
}
}
}
You can find here a lot of good patterns which I personally use on my projects and how also JWT token handling.
For saving token in a brower, you can use cookie, sessionStorage or localStorate, last one is the most popular now (short explination here).
In a few words, you can create an axion instance and add a token before request sent.
const http = axios.create({
baseURL: process.env.VUE_APP_SERVER_API,
// here you can specify other params
})
http.interceptors.request.use(request => {
// Do something before request is sent
request.headers['Authorization'] = `JWT ${TOKEN_HERE}`
// some logic what to do if toke invalid, etc ...
return request
}, function (error) {
// Do something with request error
return Promise.reject(error)
})
I tried fetch to call api and passing credentials "include" to header which set cookies from server initially but on page refresh cookies got lost.
public post = async (payload:any, endpoint: string):Promise<any> => {
return new Promise((resolve, reject) => {
console.log(${config.baseUrl}${endpoint})
const URL = ${config.baseUrl}${endpoint};
fetch(URL, {
credentials: 'include',
method: 'POST',
body: JSON.stringify(payload),
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then(data => data.json())
.then((data:any) => {
console.log("data", data)
const responsePayload = {
statusCode: data.status,
data: data
};
resolve(responsePayload);
})
.catch((error:any) => {
if (error.response === undefined) {
const errorpayload = {
statusCode: 503,
title: 'network error occured',
parameter: 'Network Error',
};
reject(errorpayload);
} else {
const errors = error.response.data.errors;
const errorPayload = {
statusCode: error.response.status,
data: error.response.data.errors,
};
reject(errorPayload);
}
});
});
};
Better read cookies on login and store it to loaclstorage and from there you can use it the way you want.
I have stripe async code in my React app, and trying to add error handling in my code but have no idea how to handle it. i know how to do it with .then() but async/await is new to me
EDITED
added .catch() i got errors in network tab in response tab.
but i can log it to console?
submit = async () => {
const { email, price, name, phone, city, street, country } = this.state;
let { token } = await this.props.stripe
.createToken({
name,
address_city: city,
address_line1: street,
address_country: country
})
.catch(err => {
console.log(err.response.data);
});
const data = {
token: token.id,
email,
price,
name,
phone,
city,
street,
country
};
let response = await fetch("/charge/pay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
}).catch(err => {
console.log(err.response.data);
});
console.log(response);
if (response.ok)
this.setState({
complete: true
});
};
thanks
Fetch detects only network errors. Other errors (401, 400, 500) should be manually caught and rejected.
await fetch("/charge/pay", headers).then((response) => {
if (response.status >= 400 && response.status < 600) {
throw new Error("Bad response from server");
}
return response;
}).then((returnedResponse) => {
// Your response to manipulate
this.setState({
complete: true
});
}).catch((error) => {
// Your error is here!
console.log(error)
});
If you are not comfortable with this limitation of fetch, try using axios.
var handleError = function (err) {
console.warn(err);
return new Response(JSON.stringify({
code: 400,
message: 'Stupid network Error'
}));
};
var getPost = async function () {
// Get the post data
var post = await (fetch('https://jsonplaceholder.typicode.com/posts/5').catch(handleError));
// Get the author
var response = await (fetch('https://jsonplaceholder.typicode.com/users/' + post.userId).catch(handleError));
if (response.ok) {
return response.json();
} else {
return Promise.reject(response);
}
};
You can either use try/catch just like normal, imperative programming:
try {
let response = await fetch("/charge/pay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
} catch(error) {
// Error handling here!
}
Or you can mix-and-match .catch() just like you do with promises:
let response = await fetch("/charge/pay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
}).catch(function(error) {
// Error handling here!
});
Wrap your await with try catch.
try {
let response = await fetch("/charge/pay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
console.log(response);
} catch (error) {
console.log(error);
}
This works if server returns { message: "some error" } but I'm trying to get it to support res.statusText too:
const path = '/api/1/users/me';
const opts = {};
const headers = {};
const body = JSON.stringify({});
const token = localStorage.getItem('token');
if (token) {
headers.Authorization = `Bearer ${token}`;
}
try {
const res = await fetch(path, {
method: opts.method || 'GET',
body,
headers
});
if (res.ok) {
return await (opts.raw ? res.text() : res.json());
}
const err = await res.json();
throw new Error(err.message || err.statusText);
} catch (err) {
throw new Error(err);
}
async function loginWithRedirect(payload: {
username: string;
password: string;
}) {
const resp = await (await fetch(`${env.API_URL}/api/auth/login`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(payload),
credentials: "include",
})).json();
if (resp.error) {
dispatch({type: "ERROR", payload: resp.error.message});
} else {
dispatch({type: "LOGIN", payload: resp});
}
}
If response.ok is false you can throw an error then chain catch method after calling your function as follows
async function fetchData(){
const response = await fetch("/charge/pay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
if(!response.ok){
const message = `An error occured: ${response.status}`;
throw new Error(message);
}
const data = await response.json();
return data;
}
fetchData()
.catch(err => console.log(err.message));
I write promise function for using fetch in async await.
const promisyFetch = (url, options) =>
new Promise((resolve, reject) => {
fetch(url, options)
.then((response) => response.text())
.then((result) => resolve(result))
.catch((error) => reject(error));
});
By the way i can use it easly in async with try catch
const foo = async()=>{
try {
const result = await promisyFetch('url' requestOptions)
console.log(result)
} catch (error) {
console.log(error)
}
}
It was simple example, you could customize promisyFetch function and request options as you wish.
const data = {
token: token.id,
email,
price,
name,
phone,
city,
street,
country
};
axios
.post("/charge/pay", data)
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err.response.data);
});