How to get data correctly using the Spotify API with React - api

I have the following problem when I request data from the Spotify API, at first I get it, but when I reload the page or try to write this state using useState, an error 400 or 401 occurs. The code I use to get the data:
`
import axios from 'axios';
const BASE_URL = 'https://api.spotify.com/v1';
export const fetchFromAPI = async (url: string, token: string) => {
const { data } = await axios.get((`${BASE_URL}/${url}`), {
method: 'GET',
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
},
});
return data;
}
`
Next, I use the fetchFromAPI function:
`
const { token } = useContext(Context);
const [albums, setAlbums] = useState<string[]>([]);
useEffect(() => {
fetchFromAPI('browse/new-releases', token)
.then((data) => console.log(data));
}, [token])
`
I've tried logging out of my account and back in, I've also tried other links to get data but it's always the same problem. I also checked if the token is present before requesting the data and it is

Ok, I managed to find and solve this error myself.
The error was that I didn't have a user token yet, but useEffect was already starting to receive data.
useEffect(() => {
if (token) {
fetchNewReleases();
fetchFeaturedPlaylists();
fetchCategories();
fetchRecommendations();
} else {
console.log('error');
}}, [token])
For example, this piece of code will print an error twice, and only after that I receive a token and can receive data from the API.
To be honest, I didn't know how to run useEffect only when I have a token, so I solved it in a simpler way, but I don't know if it's completely correct, I have the following condition Object.values(state).length) !== 0 and if it is true, only then will I display the data from the API

Related

Next.js 500 error and Axios error when calling API that uses environment variables

I'm building a wine pairing app in Next.js—where when a user clicks on a type of wine (i.e. chardonnay), it calls an API to return the suggested food pairings. This works with no issue when I use NEXT_PUBLIC for my environment variables, but I don't want to expose my private API key.
This is my first time using server-side environment variables with Next.js I understand that this needs to happen within the pages/api folder. API routing with Next.js is still something that I'm learning, so I've been following the docs, and I also found this tutorial which I followed that resulted in the 500 (Internal Server Error). I'm also getting an Axios error. This is a screenshot of both errors—please let me know if anything should be expanded, and I'll post another screenshot.
I also understand that I can use getStaticProps(), but this call is coming from a component rather than a page, and I understand from the docs that getStaticProps() must be called from a page.
This is what my .env.local looks like:
API_KEY=<my api key>
BASE_URL=https://api.spoonacular.com/
This is what my API call looks like (pages/api/wineWithFood.js)
import axios from 'axios';
export default async function wineWithFood(req, res) {
const {
query: { wine },
} = req;
const url = `${process.env.BASE_URL}food/wine/dishes?wine=${wine}&apiKey=${process.env.API_KEY}`;
const response = await axios.get(url);
res.status(200).json({
data: response.data,
});
}
This is the relevant code for what that call looks like in my component which is properly imported into the page where it belongs:
const getPairing = async () => {
axios.get(`/api/wineWithFood?wine=${wine}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((response) => response)
.then((response) => {
setData(response.data.pairings)
})
.catch((err) => console.log(err))
}
const handleChange = (e) => {
e.preventDefault();
setWine(e.target.value);
getPairing();
};
console.log(wine)
I see that the request isn't capturing the wine type, but when I console.log the wine, it's showing up in the browser console as expected. When I console.log the response.data from the API call, I get a status code of 400 with a message stating that the wine must not be empty.
Now, if I change my code to the following—I get the same errors and console.logs as I mentioned... but only on the first try! On the second try (clicking the same exact wine), the wine shows correctly in the browser console since I'm console.loging it, but I get that same 500 error in my console, however, now I can see all of the correct data in my terminal! That leads me to believe I'm doing something wrong on the frontend. Here's the tweaked code that results in this:
const getPairing = async (wine) => {
axios
.get(`/api/wineWithFood?wine=${wine}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((response) => response)
.then((response) => {
setData(response.data.pairings);
console.log(response.data.pairings);
})
.catch((err) => console.log(err));
};
const handleChange = (e) => {
e.preventDefault();
setWine(e.target.value);
getPairing(wine);
};
I'm happy to check out any other resources to help me out if that's a better answer to this question.
I finally figured this out and wanted to share the answer—which was staring me in the face.
I left my API call in pages/api/wineWithFood.js the same. I was right. The error was on the frontend. I got rid of the getPairing() function and put everything in the handleChange function. When I console logged my response on the frontend, I realized that the info I needed was res.data.data.pairings. I also changed the axios call by using e.target.value as the search query. I removed wine and setWine since it wasn't necessary. Here's the final code:
const handleChange = (e) => {
e.preventDefault();
axios
.get(`/api/wineWithFood?wine=${e.target.value}`, {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
},
})
.then((res) => {
setData(res.data.data.pairings);
});
};
I hope this can help someone out—also I'm open to feedback if there's a better way.

How do I pull data out of response or cache for apollo client 3

I am trying to pull a jwt out of a loginUser mutation and store it in a variable then use the apollo-link to setContext of the header via "Authorization: Bearer ${token} for authentification as all my other mutations and queries require the token. I have been slamming the docs for days on Apollo Client(React) -v 3.3.20. I have been through all the docs and they show all these examples of client.readQuery & writeQuery which frankly seem to just refetch data? I don't understand how you actually pull the data out of the response and store it in a variable.
The response is being stored in the cache and I have no idea how to take that data and store it in a token variable as I stated above. Which remote queries I can just access the returned data via the data object from the useQuery hook, however on the useMutation hook data returns undefined. The only thing I could find on this on stack overflow was the my data type may be custom or non-traditional type but not sure if that is the problem.
[Cache in apollo dev tools][1]
[Mutation in apollo dev tools][2]
[Response in network tab][3]
Here is my ApolloClient config:
const httpLink = createHttpLink({ uri: 'http://localhost:4000/',
// credentials: 'same-origin'
});
const authMiddleware = new ApolloLink((operation, forward) => {
const token = localStorage.getItem('token');
// add the authorization to the headers
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
authorization: `Bearer ${token}` || null,
}
}));
return forward(operation);
})
const client = new ApolloClient({
cache: new InMemoryCache(),
link: concat(authMiddleware, httpLink),
});
The header works obviously I just can't grab the token to pass so the header just sends Authorization: Bearer.
For the login I have this:
const LOGIN_USER = gql`
mutation($data:LoginUserInput!) {
loginUser(
data: $data
) {
user {
id
name
}
token
}
}
`;
const [loginUser, { data, loading, error }] = useMutation(LOGIN_USER);
if (loading) return 'Submitting...';
if (error) return `Submission error! ${error.message}`;
Originally I was just calling
onClick={loginUser( { variables })}
For the login but onComplete never works and everywhere I look I see lots of posts about it with no solutions. So I tried slamming everything into a function that I then called with loginUser inside it:
const submit = async () => {
loginUser({ variables})
// const { user } = await client.readQuery({
// query: ACCESS_TOKEN,
// })
// console.log(`User : ${JSON.stringify(user)}`)
const token = 'token';
const userId = 'userId';
// console.log(user);
// localStorage.setItem(token, 'helpme');
// console.log({data});
}
At this point I was just spending hours upon hours just trying mindless stuff to potentially get some clue on where to go.
But seriously, what does that { data } in useMutation even do if it's undefined. Works perfectly fine for me to call data.foo from useQuery but useMutation it is undefined.
Any help is greatly appreciated.
[1]: https://i.stack.imgur.com/bGcYj.png
[2]: https://i.stack.imgur.com/DlzJ1.png
[3]: https://i.stack.imgur.com/D0hb3.png

Put API KEY in axios

I just started learn React but I have problem when I trying to make a request to the CoinMarketCap API with axios and tried several ways to set my API key. I have also also tried on Postman but message appears API key missing.
export const apiBaseURL = 'https://pro.coinmarketcap.com';
Tried like this
dispatch({ type: FETCHING_COIN_DATA })
return axios.get(`${apiBaseURL}/v1/cryptocurrency/map`,
{ headers =
{ 'X-CMC_PRO_API_KEY': 'apicode', }
})
this
dispatch({ type: FETCHING_COIN_DATA })
let config = { 'X-CMC_PRO_API_KEY': 'apicode' };
return axios.get(`${apiBaseURL}/v1/cryptocurrency/map`, { headers: config })
and this
dispatch({ type: FETCHING_COIN_DATA })
return axios.get(`${apiBaseURL}/v1/cryptocurrency/map?X-CMC_PRO_API_KEY=apicode`)
Thank you
The short answer to adding an X-Api-Key to an http request with axios can be summed up with the following example:
const url =
"https://someweirdawssubdomain.execute-api.us-east-9.amazonaws.com/prod/custom-endpoint";
const config = {
headers: {
"Content-Type": "application/json",
},
};
// Add Your Key Here!!!
axios.defaults.headers.common = {
"X-API-Key": "******this_is_a_secret_api_key**********",
};
const smsD = await axios({
method: "post",
url: url,
data: {
message: "Some message to a lonely_server",
},
config,
});
Adding the key to the default headers was the only way I could get this to work.
Use CMC_PRO_API_KEY as a query parameter, instead of X-CMC_PRO_API_KEY:
dispatch({ type: FETCHING_COIN_DATA })
return axios.get(`${apiBaseURL}/v1/cryptocurrency/map?CMC_PRO_API_KEY=apicode`)
I realize this has been solved; for the sake of alternatives here is how I did it. I created a instance of axios in /includes/axios:
import axios from "axios";
const instance = axios.create({
baseURL: "https://example.com/api",
headers: {
"Content-Type": "application/json",
"x-api-key": "****API_KEY_HERE****",
},
});
export default instance;
Now axios can be imported anywhere in the project with the give configuration. Ideally you want to add your secret to the ENV variable for security reasons.

handle network request failed in react native

I'm facing an issue while using react native fetch api. many times request got failure . I have a high speed connection. but many times it got failed.
that issue is happening In android,ios both.
const shoppingApi = 'myserverlink';
async function Sendshoppinapi(data) {
try {
let response = await fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
});
let responseJson = await response.json();
return responseJson;
}
catch (error) {
Alert.alert(error.toString())
}
}
export {Sendshoppinapi};
data that I sending server as post request
add_to_wishlist = (item,index) => {
{
let data = new FormData();
data.append('methodName', 'add_to_wishlist');
data.append('user_id', global.userid)
data.append('item_id', this.props.navigation.state.params.itemid.toString())
Sendshoppinapi(data).then((responseJson)=>{
console.warn(responseJson);
if(responseJson.responseCode == '200'){
this.setState({fav:false})
Alert.alert('SHOPPING','Item added to wishlist successfully.',[{text: 'OK',},],{ cancelable: false })
}
else{
this.setState({fav:false})
Alert.alert('SHOPPING','Item already .',[{text: 'OK',},],{ cancelable: false })
}
})}
}
Error that when request got failed
I've quoted an answer I used for another post - however I have added await.
You can check the status of the call, to determine perhaps why the network call failed. Try using fetch's ok to check whether the response was valid, for example:
.then(function(response) {
if (!response.ok) {
//throw error
} else {
//valid response
}
})
Using await:
let response = await fetch(url)
if (response.ok) return await response.json()
You can also access the response's status like:
response.status;
or also, statusText such as:
response.statusText;
checkout the below:
https://developer.mozilla.org/en-US/docs/Web/API/Response/statusText
https://developer.mozilla.org/en-US/docs/Web/API/Response/status
https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
Use then() function with promises. (Requested code snippet)
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
//resp contains your json data
});
You also can make your function returns a Promise, and use it with then():
function sendShoppingApi(data) {
return new Promise((resolve, reject) => {
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json();
})
.then((resp) => {
resolve(resp);
/*
you should also check if data is valid, if something went wrong
you can reject the promise:
if(!dataOK)
reject("error message");
*/
});
});
}
So now you can do something like this:
sendShoppingApi(data)
.then((resp) => {
//do stuff with your data
})
.catch((err) => {
//handle error
});
UPDATE
could be a duplicate of this: React Native fetch() Network Request Failed
For the case when you are running the app on the android device, the API is on a computer and both of them are on the same network I have added some possible things to check. I haven't detailed specific solutions since there are many answers on each topic.
Do a quick check with ngrok https://ngrok.com/ on the free plan to see if that works. If yes:
Make sure the API is accessible by trying to access it on the device browser (most important is to check if you allow the port at inbound rules, firewall).
If you are using HTTPS, you might get an error if your react native env is not properly configured to accept not trusted certificates, assuming you are using a non trusted one. Do a check without HTTPS, only with HTTP, to see if it's the case. https://github.com/facebook/react-native/issues/20488

Automatically log out user when token is invalidated

I have a SPA that is built on vuejs. When a user is logged in via API, the token is stored in local storage.
I need a global solution which will logout and prompt the user when the token is no longer valid. At the moment, I get "invalid token" error when accessing private API endpoints.
How do I rig axios so that ALL response of invalid tokens will trigger the logout/prompt code?
Here is an simple example with axios. It use a Bearer token for authentification.
import axios from "axios";
import { useUserStore } from "#/store/userStore";
const apiClient = axios.create({
baseURL: ""http://127.0.0.1:8001",
headers: {},
});
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
const config = error?.config;
if (error?.response?.status === 401) {
const result = await refreshToken();
if (result) {
config.headers = {
...config.headers,
authorization: `Bearer ${result?.token}`,
};
}
return axios(config);
}
);
const refreshToken = async () => {
/* do stuff for refresh token */
// if refresh token failed
try {
useUserStore().actionLogout();
} catch (error) {
console.log(error);
} finally {
loacalStorage.clear();
}
};
you can write a function that clears your local storage after some time and logout user