i'm training to do an axios call with promise, but wihtout success
import axios from "axios";
export class ContiService {
getConti() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
const URL =
process.env.REACT_APP_BASE_PATH + process.env.REACT_APP_RESOURCE_CONTI;
console.log(URL);
return axios(URL, {
method: "GET",
headers: { "content-type": "application/json", preferences: "" },
});
resolve(/* ??? */);
}, 300);
});
}
}
export default class Posizionecliente extends Component {
constructor(props) {
super(props);
this.contiService = new ContiService();
this.state = {
conti: [],
};
}
componentDidMount() {
this.contiService.getConti().then(r => this.setState({ conti: r.data.conti }));
}
}
How can complete this with promise?
The call does not work at the moment, where should I place resolve?
The key here is to realize that axios itself returns a promise. So there are a couple of ways to approach this.
In either case, there is no need for setTimeout.
return new Promise(function(resolve, reject) {
const URL = process.env.REACT_APP_BASE_PATH+process.env.REACT_APP_RESOURCE_CONTI;
axios(URL, {
method: 'GET',
headers: {'content-type': 'application/json', 'preferences' : ''}
}
)
.then(response=> resolve(response.data))
.catch(err => reject(err))
}
Or alternately, and perhaps more clearly, simply return the axios call itself, which will return the promise and allow your component logic to handle state accordingly
getConti() {
const URL = process.env.REACT_APP_BASE_PATH+process.env.REACT_APP_RESOURCE_CONTI;
return axios(URL, {
method: 'GET',
headers: {'content-type': 'application/json', 'preferences' : ''}
}
)
}
You don't have to change your component logic (except perhaps to handle promise rejections).
this.contiService.getConti().then(r => this.setState({ conti: r.data.conti }));
The .then will handle the successful completion of the axios call.
Here's a good read on Promise Chaining. See in particular, the section on returning promises.
If a promise is resolved with another promise, it will adopt the new one. Therefore:
const request = axios(URL, etc);
resolve(request);
Don't return. setTimeout doesn't do anything with the return value of the function it executes.
Related
I'm working on load testing my API, but at some point I make a call to a different API.
Since I don't want to stress the second one, whenever I'm load testing I want to set a timeout and return an OK response like this:
function sendMessage(requestLib, blockApi, logger) {
if(!blockApi){
return (*my params*) => requestLib(`someURL`, {
headers: { Authorization: `Bearer ${token}` },
method: 'post',
data
});
}else{
logger.info("About to use the promise");
const response = returnOk.then(function() {
return new Response(200, {}, null, 'dummy.com');
});
return response;
}
}
returnOk is a Promise I defined earlier this way:
const returnOk = new Promise((resolve, reject) => {
setTimeout( function() {
resolve("Success!")
}, 2000)
});
And the function sendMessage is called inside a different function like this:
module.exports = ({ requestLib, Logger }) => async function(req, res) {
// Some unrelated code to decide if I'll call sendMessage
const response = await sendMessage(requestLib, blockApi, logger)(params);
// I log the response
res.end();
}
The regular flow works like a charm, but when I'm load testing and I get to returnOk.then()...
It throws
sendMessage(...) is not a function
If I remove the timeout and just return
return new Response(200, {}, null, 'dummy.com');
Things work just fine.
For some reason, your sendMessage(…) function returns another function:
return (*my params*) => …
You will need to do the same when mocking sendMessage:
function sendMessage(requestLib, blockApi, logger) {
if (!blockApi) {
return (*my params*) => requestLib(`someURL`, {
headers: { Authorization: `Bearer ${token}` },
method: 'post',
data
});
} else {
return (*my params*) => {
const returnOk = new Promise((resolve, reject) => {
setTimeout(resolve, 2000)
});
logger.info("About to use the promise");
return returnOk.then(function() {
return new Response(200, {}, null, 'dummy.com');
});
};
}
}
Btw, you really shouldn't have this boolean blockApi parameter to sendMessage. Write two distinct functions with the same signature, and use dependency injection in the code that is calling it.
I'm making a GET request with Axios in a React-Redux project, and I get the following error:
TypeError: "config.method.toLowerCase is not a function"
request Axios.js:43
wrap bind.js:11
apiCall api.js:32
apiCall api.js:29
... ...
api.js is a file from my own project. bind.js and Axios.js is from the Axios library. This is my api function:
export function apiCall(method, path, data){
let url = backendDomain + path
let config = {
method: [method],
url: [url],
data : [data],
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
}
}
return new Promise((resolve, reject)=>{
return axios(config).then(res=> {
return resolve(res.data)
}).catch(err => {
return reject(err.response);
})
})
The function that makes use of apiCall() is this function:
export function authUser(url, userData, method){
return (dispatch) => {
return new Promise((resolve, reject)=>{
return apiCall(method, "/"+`${url}`, userData)
.then((data) => {
...
resolve();
})
.catch(err=>{
...
reject();
})
})
}
}
Do you think there's something wrong with my code or is there something wrong with the library? When I use authUser to dispatch my action (for Redux State), I double checked that "method" is a String, I console.logged typeof method in api.js, and it returned string.
Edit:
I tried calling toString() to the method parameter passed into apiCall(), but it didn't work:
let reMethod = method.toString();
const config = {
method: [reMethod],
url: [url],
data : [data],
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
}
}
As mentioned in my comment, you're providing an array when axios expects a string:
const config = {
method: reMethod,
url: url,
data : data,
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
}
}
I have a Mobile App that either uses a cloud server or a local server to serve information.
In my App.js I have:
helperUtil.apiURL().then((url) => {
global.API_URL = url;
})
The function does something like:
export async function apiURL() {
try {
var local = await AsyncStorage.getItem('local')
local = (local === 'true')
if(typeof local == 'undefined') return "https://api.website.com";
else if(!local) return "http://192.168.0.6:8080";
else return "https://api.website.com";
}
catch(err) {
return "https://api.website.com";
}
}
Then my fetch command would be:
fetch(global.API_URL+'/page', {
method: 'GET',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer '+this.state.authtoken },
})
I'm running into problems here where the API_URL ends up undefined so I feel like there might be a better solution to this.
Open to any and all suggestions. Thank you.
Insted of seetting url in global obj always use method which return a Promise, and it will return your global object if exist and if not get data from apiURL function. With async/await syntax fetch will be executed only when getAPI promise will be resolved and there will be no situation that url is empty.
const getAPI = () => {
return new Promise((resolve, reject) =>{
if(global.API_URL) {
resolve(global.API_URL)
} else {
helperUtil.apiURL().then((url) => {
global.API_URL = url;
resolve(url)
})
}
});
const fetchFunc = async () => {
const url = await getAPI()
fetch(url+'/page', {
method: 'GET',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer
'+this.state.authtoken },
})
}
It gets complicated to me when I mix the promise with subscribe and another async task together.
This is my auth service:
getCurrentUserToken(){
return new Promise((resolve,reject)=>{
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
resolve(idToken)
}).catch(function(error) {
reject(error)
});
})
}
This is my HTTP service:
sendEmail(email) {
return this.authService.getCurrentUserToken().then(token => {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Basic server-Password',
})
};
let data = email
data['idToken'] = token
return this.http.post(this.apiServer + 'sendEmail', data, httpOptions)
})
}
This is how I call the sendEmail(email) function at the component:
Observable.fromPromise(this.httpService.sendEmail(element)).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
))
I have to pass currentUserToken to the API to let the API authenticate the user session. Still, both of the the getCurrentUserToken() sendEmail() are running in async, so I have to use Promise to pass the Token to sendEmail() function, and let the sendEmail function to call the API to send the email.
Without the promise, I am able to subscribe to the http.post like this:
this.httpService.sendEmail(element).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
))
Unfortunately, I screwed it up when I added the promise into it, and the console.log is returning this:
Observable {_isScalar: false, source: Observable, operator: MapOperator}
Please advise on how to subscribe to the http.post that is placed inside the Promise.
There's seriously no need of Complicating things here.
I'll use async/await syntax here and for that, we'll have to work with Promises instead of Observables. Good thing is, we can leverage the toPromise() method on an Observable value to change it to a Promise
Focus on my comments in the code as well
Here's the implementation
For getCurrentUserToken
getCurrentUserToken() {
return firebase.auth().currentUser.getIdToken(true);
// This will already return a Promise<string>
// So no need to do a .then and then return from there.
}
For sendEmail
async sendEmail(email) {
// Since getCurrentUserToken returns a Promise<string> we can await it
const token = await this.authService.getCurrentUserToken();
// token will now have the Current User Token
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Basic server-Password',
})
};
let data = email
data['idToken'] = token
return this.http.post(this.apiServer + 'sendEmail', data, httpOptions).toPromise();
// Notice how we're calling the .toPromise() method here
// to change Observable into a Promise
}
How to use it?
This code will go in your Component Method where you were previously calling this.httpService.sendEmail. DO MAKE SURE TO MARK THAT FUNCTION AS async THOUGH.
// We can only await something in a function which is declared of type async
async sendEmail() {
try {
const data = await this.httpService.sendEmail(element);
// Since sendEmail again returns a Promise, I can await it.
console.log(data);
} catch (error) {
console.log(error);
}
}
Why don't we use Observable instead of Promises here.
getCurrentUserToken() {
return new Observable(obs => {
firebase
.auth()
.currentUser.getIdToken(/* forceRefresh */ true)
.then(function(idToken) {
obs.next(idToken);
obs.complete();
})
.catch(function(error) {
obs.error(error);
});
});
}
sendEmail(email): Observable {
return new Observable(obs => {
this.authService.getCurrentUserToken().subscribe(token => {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Basic server-Password'
})
};
let data = email;
data['idToken'] = token;
this.http
.post(this.apiServer + 'sendEmail', data, httpOptions)
.subscribe(
result => {
obs.next(result);
obs.complete();
},
error => {
obs.error();
}
);
});
});
}
// now call the service from Component like this.
this.httpService.sendEmail(element).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
));
I'm having some problems getting a response from a chained Promise.
I have my component where the chain starts
component
componentDidMount = async ()=> {
try{
const products = await post('payments/getProducts', {});
console.log(products);
} catch(e) {
console.log(e)
}
}
This component calls my API helper:
async function post(url, data) {
token = null;
if (firebase.auth().currentUser) {
token = await firebase.auth().currentUser.getIdToken();
}
try {
const response = axios({
method: 'POST',
headers: {
Authorization: `${token}`,
},
data,
url: `${API_URL}${url}`,
})
return response;
} catch(e){
Promise.reject(e);
}
}
and my API Helper then calls a Firebase Cloud Function which calls Stripe:
paymentRouter.post('/getProducts', (req, res) => {
return stripe.products.list()
.then(products => {
console.log(products.data)
return products.data;
})
.catch(e => {
throw new functions.https.HttpsError('unknown', err.message, e);
})
})
Calling the function is no problem, and my Cloud Function logs out the product data, but I can't get the response to log in my API Helper nor my component.
Promise.reject(e);
That is completely senseless as it creates a new rejected promise that is not used anywhere. You could await it so that it gets chained into the promise returned by the async function, or you just return the promise from axios:
async function post(url, data) {
let token = null; // always declare variables!
if (firebase.auth().currentUser) {
token = await firebase.auth().currentUser.getIdToken();
}
return axios({
method: 'POST',
headers: {
Authorization: `${token}`,
},
data,
url: `${API_URL}${url}`,
});
}
Now the errors don't go into nowhere anymore and you can probably debug the problem :)