I have following Node.js script.
const axios = require('axios');
const https = require('https');
const postRequest = (url, data) => {
gLogger.debug('postRequest started');
// try {
const headers = {
'Content-Type': 'application/json',
'Content-Length': JSON.stringify(data).length,
};
const load = {
headers,
httpsAgent: agent,
};
gLogger.debug(`postRequest load: ${JSON.stringify(load)}`);
const result = axios.post(url, data, load).then((response) => {
return result;
})
.catch((error) => {
return error;
});
};
And this is for unit test:
const axios = require('axios');
const personalRecords = {
data: { peopleDomain: { paom: { data: { persons: [], delta: 1, recordsFetched: 10 } } } },
};
const tockenData = {
data: {
access_token: 'access_token',
expires_in: 1000,
},
};
// jest.useFakeTimers();
jest.setTimeout(8000);
jest.mock('axios', () => ({
post: jest.fn().mockReturnValue(tockenData),
get: jest.fn().mockReturnValue(personalRecords),
defaults: jest.fn().mockReturnValue(),
}));
The problem when I am running unit test yarn test, I keep getting the following error:
TypeError: axios.post(...).then is not a function.
What is the problem and how to fix it?
This is because you mock post function to be a function that returns a value instead of a promise. Remember post returns promise
This is the line that causes trouble:
post: jest.fn().mockReturnValue(tockenData),
To mock axios, there is an answer here:
How do I test axios in Jest?
When useEffect is executed in my code, I want to get fcmtoken through firebase .getToken and send fcmtoken to body of auth/me router.
but if i use my code this error occure
Unhandled Promise Rejection (id: 0):
ReferenceError: fcmtoken is not defined
Perhaps the cause of the error is incorrect use of async await or anything
but how can i fix my code?
this is my code
useEffect(() => {
Linking.addEventListener('url', async ({url}) => {
var newURL = url;
var splitURL = newURL.toString().split('=');
const token = splitURL[1];
messaging()
.getToken()
.then((fcmtoken) => {
return fcmtoken;
});
const {data} = await axios.post(
'/auth/me',
{fcmtoken},
{
headers: {Authorization: `Bearer ${token}`},
},
);
console.log('data::::', data);
AsyncStorage.setItem('tokenstore', `${token}`, () => {
console.log('유저 닉네임 저장 완료');
});
dispatch({
type: KAKAOLOG_IN_REQUEST,
data: data,
});
});
return () => Linking.removeEventListener('url');
}, []);
you are trying to send an undefined variable 'fcmtoken' to the API. In the code bellow I changed the way you get the fcm token.
useEffect(() => {
Linking.addEventListene`enter code here`r('url', async ({url}) => {
var newURL = url;
var splitURL = newURL.toString().split('=');
const token = splitURL[1];
let fcmtoken = await messaging().getToken();
const {data} = await axios.post(
'/auth/me',
{fcmtoken},
{
headers: {Authorization: `Bearer ${token}`},
},
);
console.log('data::::', data);
AsyncStorage.setItem('tokenstore', `${token}`, () => {
console.log('유저 닉네임 저장 완료');
});
dispatch({
type: KAKAOLOG_IN_REQUEST,
data: data,
});
});
return () => Linking.removeEventListener('url');
}, []);
I am trying to catch a 404 error I receive for some of my API calls.
I am using the matrix API and need to get room names from their id's, which is working. Some of them don't have names which returns a 404 error I am trying to catch. Unfortunately this dosn't work and I don't understand where I'm going wrong. The code itself exectues, but it still throws 404's in the console.
const JoinedRooms = () => {
const [answer, setAnswer] = useState([]);
const [isError, setIsError] = useState(false);
const getAnswer = async () => {
const res = await fetch(`https://example.com/_matrix/client/r0/joined_rooms`, {
headers: {
"Authorization": `Bearer ${localStorage.getItem('mx_access_token')}`
}
});
const answer = await res.json();
const getNames = await Promise.all(answer.joined_rooms.map(async (roomId) => {
setIsError(false);
const res = await fetch(`https://example.com/_matrix/client/r0/rooms/${roomId}/state/m.room.name`, {
headers: {
"Authorization": `Bearer ${localStorage.getItem('mx_access_token')}`
}
});
try {
const roomName = await res.json();
return roomName.name;
} catch (error) {
setIsError(true);
}
}));
setAnswer(getNames);
}
useEffect(() => {
getAnswer();
}, []);
return answer;
}
export default JoinedRooms;
A 404 status code will not throw an error, so your try...catch block won't catch it. Try getting the 404 directly from the res object returned.
const res = await fetch(`https://example.com/_matrix/client/r0/rooms/${roomId}/state/m.room.name`, {
headers: {
"Authorization": `Bearer ${localStorage.getItem('mx_access_token')}`
}
});
if (res.status === 404) {
// handle error
}
If I put my config as the second argument, my cancellation token (third arg) gets ignored. However I need the Authorization header because this get request will be (but isn't yet!) Behind authentication middleware on my node API. So my question is: Where do I put my config?
const config = {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
};
const getBoards = async (cancelToken) => {
try {
if (localStorage.getItem("token") == null) {
throw new Error();
}
const response = await Axios.get("/boards", {
cancelToken: cancelToken.token,
});
setBoards(response.data);
} catch (e) {}
};
useEffect(() => {
const request = Axios.CancelToken.source();
getBoards(request);
return () => {
request.cancel();
};
}, []);
pass them in the same object.
const response = await Axios.get("/boards", {
headers: {},
cancelToken: cancelToken.token,
});
I have made a wrapper for fetch function for my API calls in react-native. I dont want to pass JWT token everytime that I make an API call, so I thought that fetching it inside wrapper will fix it for me, but I cannot get it to work because of async nature...
useFetch.js
// import qs from "querystring";
import { getUserAuthToken } from "../storage";
const responseChecker = async (response) => {
let error = "";
let data = {};
let statusCode = null;
if (!response.ok) {
error = "Something went wrong";
statusCode = response.status;
} else {
statusCode = response.status;
data = await response.json();
}
return { statusCode, error, data };
};
const fetchAuthToken = getUserAuthToken();
const useFetch = (baseURL, authHeader = null) => {
console.log(fetchAuthToken);
**//Cannot get this token in time for the API call ^**
const defaultHeader = {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
key: "1c419c7e-3a34-49f0-9192-b48d4534dff3",
Authorization: authHeader ? authHeader :fetchAuthToken,
};
const customFetch = (
url,
method = "GET",
body = false,
headers = defaultHeader,
) => {
const options = {
method,
headers,
credentials: "include",
};
if (body) options.body = body;
return fetch(url, options);
};
const get = async (endpoint) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "GET");
return responseChecker(response);
};
const post = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "POST", body);
return responseChecker(response);
};
const put = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "PUT", body);
return responseChecker(response);
};
return {
get,
post,
put,
};
};
export default useFetch;
storage.js
import AsyncStorage from "#react-native-community/async-storage";
export const getUserAuthToken = async () => {
try {
const userToken = await AsyncStorage.getItem("userAuthToken");
return userToken;
} catch (e) {
console.log("error");
}
};
exportAPI.js
import useFetch from "./fetch";
const LOCAL_IP = "192.168.0.131";
export const authAPI = (header) => useFetch(`http://${LOCAL_IP}:8000`, header);
export const activityAPI = useFetch(`http://${LOCAL_IP}:8000`);
Steps.js
import React, { useEffect, useState } from "react";
import { Text, Platform } from "react-native";
import { CardXLarge } from "../../../components/Cards/";
import fitnessKitApis from "../../../utilities/fitnessKits";
import { activityAPI } from "../../../utilities/apis";
const StepCard = (props) => {
const fetchStepsFromFitnessKits = async () => {
if (Platform.OS === "android") {
await fitnessKitApis.historicSteps().then((res) => {
setSteps(res);
});
} else {
await fitnessKitApis.historicSteps((result) => setSteps(result));
}
};
const [steps, setSteps] = useState(0);
useEffect(() => {
fetchStepsFromFitnessKits();
const requestParams = { date: new Date(), steps };
const { data, statusCode, error } = activityAPI.get(
"/v1/user/steps/",
requestParams,
);
// console.log(data, statusCode, error);
}, [steps]);
return (
<CardXLarge>
<Text>{steps}</Text>
</CardXLarge>
);
};
export default StepCard;
I know I can pass authHeader from component but that will result in adding 2-3 lines of code in every component which is not super convenient.
Thanks
If you don't want to use async/await in a function to get items from asyncStorage. You can use either callback or promise in place of async/await.
Callback:
AsyncStorage.getItem('data1', (error, data1) => {
// perform your logic here.
});
Promise:
AsyncStorage.getItem('data1').then(data1=>{
// perform your logic here.
}).catch(error=>{
// handle error
})
I have personally used callback for getItem and worked perfectly. I have not tested the promise version but I expect to do the same work as callback.
...
const fetchAuthToken = getUserAuthToken();
...
Your getUserAuthToken is an asynchronous function and here it is not being awaited. To guarantee that asynchronous call is finished you have to await it or use callbacks as #HungrySoul suggested.
You can't await something outside of an asynchronous function.
Solution that I would suggest is creating a class UseFetch and passing the arguments through the constructor. The argument being here the JWT token that you are getting from the AsyncStorage.
Also, another thing that can be done and is a good practice - use redux for managing the state and keeping the JWT token. You might look into that. It will take a bit longer but it will make your code more elegant.
Edit: Or, you might try something like this.
Keep in mind that you have to wait for promises to resolve before you use what was promised.
Here we are using a closure. You will have to pass an argument (which is an async function) to the useFetchBuilder. That function will be awaited and provide the JWT. You can use the getUserAuthToken for that purpose.
Keep in mind that you have to use await or wait for the promise to resolve before using this function. Problem might be somewhere else in your code - maybe the life cycle methods.
I hope this helped.
const responseChecker = async (response) => {
let error = "";
let data = {};
let statusCode = null;
if (!response.ok) {
error = "Something went wrong";
statusCode = response.status;
} else {
statusCode = response.status;
data = await response.json();
}
return { statusCode, error, data };
};
const useFetchBuilder = async (userTokenProvider) => {
const userToken = await userTokenProvider();
return (baseURL, authHeader = null) => {
const defaultHeader = {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
key: "1c419c7e-3a34-49f0-9192-b48d4534dff3",
Authorization: authHeader ? authHeader : userToken,
};
const customFetch = (
url,
method = "GET",
body = false,
headers = defaultHeader,
) => {
const options = {
method,
headers,
credentials: "include",
};
if (body) options.body = body;
return fetch(url, options);
};
const get = async (endpoint) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "GET");
return responseChecker(response);
};
const post = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "POST", body);
return responseChecker(response);
};
const put = async (endpoint, body = {}) => {
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "PUT", body);
return responseChecker(response);
};
return {
get,
post,
put,
};
};
}
export default useFetchBuilder;
I moved getUserAuthToken function down to each request method function in useFetch function where I can await for the response. Then it all worked perfectly.. Also I could have use getUserAuthToken but usingAsyncStorage.getItem seems much cleaner
modified fetch.js
// import qs from "querystring";
import AsyncStorage from "#react-native-community/async-storage";
const responseChecker = async (response) => {
let error = "";
let data = {};
let statusCode = null;
if (!response.ok) {
error = "Something went wrong";
statusCode = response.status;
} else {
statusCode = response.status;
data = await response.json();
}
return { statusCode, error, data };
};
const useFetch = (baseURL, authHeader = null) => {
const defaultHeader = {
Accept: "application/json",
// "Content-Type": "application/x-www-form-urlencoded",
"Content-Type": "application/json",
key: "1c419c7e-3a34-49f0-9192-b48d4534dff3",
Authorization: authHeader,
};
const customFetch = (
url,
method = "GET",
body = false,
headers = defaultHeader,
) => {
const options = {
method,
headers,
credentials: "include",
};
if (body) options.body = JSON.stringify(body);
return fetch(url, options);
};
const get = async (endpoint) => {
await AsyncStorage.getItem("userAuthToken").then((result) => {
defaultHeader.Authorization = result;
});
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "GET");
return responseChecker(response);
};
const post = async (endpoint, body = {}) => {
await AsyncStorage.getItem("userAuthToken").then((result) => {
defaultHeader.Authorization = result;
});
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "POST", body);
return responseChecker(response);
};
const put = async (endpoint, body = {}) => {
await AsyncStorage.getItem("userAuthToken").then((result) => {
defaultHeader.Authorization = result;
});
const url = `${baseURL}${endpoint}`;
const response = await customFetch(url, "PUT", body);
return responseChecker(response);
};
return {
get,
post,
put,
};
};
export default useFetch;