const baseQuery = fetchBaseQuery({
baseUrl: 'url',
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token
// If we have a token set in state, let's assume that we should be passing it.
if (token) {
headers.set('authorization', `Bearer ${token}`)
}
return headers
}
})
const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions)
if (result?.error?.originalStatus === 401) {
//I noticed result.error.originalStatus always returned undefine
console.log('sending refresh token')
const refreshResult = await baseQuery({
url: '/users/generateTokens',
method: 'POST'
}, api, extraOptions)
console.log(refreshResult)
if (refreshResult?.data) {
const user = api.getState().auth.user
api.dispatch(setCredits({ ...refreshResult.data, user }))
result = await baseQuery(args, api, extraOptions)
console.log(result)
} else {
api.dispatch(logOut())
}
}
return result
}
So, my knowledge of next and JS in general is extremely minimal as is my Auth0 knowledge. I was wondering if anyone could suggest how to pass query parameters or a request body to the first block of code below?
I know I can add a body to the fetch function, but when I try to do that in the /pages/profile block below, it seems to break the request.
Even better would be to make this some kind of generic function, where I can pass in a the route, method, and body since all of the routes will be protected anyway.
Any help would be greatly appreciated.
/pages/api/my/user
import { getAccessToken, withApiAuthRequired } from '#auth0/nextjs-auth0';
export default withApiAuthRequired(async function shows(req, res) {
try {
const { accessToken } = await getAccessToken(req, res, {
scopes: ['profile']
});
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/my/user`, {
headers: {
Authorization: `Bearer ${accessToken}`
},
});
const shows = await response.json();
res.status(200).json(shows);
} catch (error) {
res.status(error.status || 500).json({ error: error.message });
}
});
And here's the snippet that fetches the above:
/pages/profile
const [state, setState] = useState({ isLoading: false, response: undefined, error: undefined });
useEffect(() => {
callApi();
}, []);
const callApi = async () => {
setState(previous => ({ ...previous, isLoading: true }))
try {
const response = await fetch(`/api/my/user`);
const data = await response.json();
setState(previous => ({ ...previous, response: data, error: undefined }))
} catch (error) {
setState(previous => ({ ...previous, response: undefined, error }))
} finally {
setState(previous => ({ ...previous, isLoading: false }))
}
};
const { isLoading, response, error } = state;
Cannot see where your actual problem is - here's a snippet that usually works for me with fetch:
provide headers and body as parameters of an options variable, add the url and you are good to go.
const res = await fetch(url, options)
const protocol = 'https'
const hostname = 'YOURHOSTNAME'
const queryParams = `foo=bar`
const body = []
const url = `${protocol}://${hostname}/api/${endpoint}?${queryParams}`;
const options = {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': `${hostname}`, // set as needed
},
body: body,
method: 'POST',
};
const res = fetch(url, options);
Hope this helps
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');
}, []);
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;