How to optimize an API fetch to be less redundant - javascript

I'm building a React App which consumes an API and what I build for now is 2 functions which both GET the same URL but changing the second part of the API Endpoint like URL/?search or URL/?i=123 but what I would like to achieve is to have less redundant code so I was wondering to make one function which just takes the same URL and change the second part of the URL based on the call.
By the way, this approach gives me problems.
The code I did originally was this:
import {API_MOVIE_URL, API_KEY} from "./ApisConst";
export const getMoviesBySearch = async search => {
try {
const url = `${API_MOVIE_URL}?apikey=${API_KEY}&${search}`;
const response = await fetch(url);
const json = await response.json();
return json;
} catch {
return {
success: false,
result: [],
message: "There is an issue to get data from server. Please try again later.",
};
}
};
export const getMoviesInfo = async movieID => {
try {
const url = `${API_MOVIE_URL}?apikey=${API_KEY}&i=${movieID}&plot`;
const response = await fetch(url);
const json = await response.json();
return json;
} catch {
return {
success: false,
result: [],
message: "There is an issue to get data from server. Please try again later.",
};
}
};
And the change I tried is:
const fetchAPI = async ({type, query}) => {
const queryParams = {
byString: `&${query}`,
byMovieId: `&i=${query}&plot`,
};
const endpoint = `${API_MOVIE_URL}?apikey=${API_KEY}${queryParams[type]}`;
console.log("fetching", endpoint);
return fetch(endpoint)
.then(res => res)
.catch(() => ({
success: false,
result: [],
message: "There is an issue to get data from server. Please try again later.",
}));
};
export const getMoviesBySearch = async search =>
await fetchAPI({type: "byString", query: search});
export const getMoviesInfo = async movieID =>
await fetchAPI({type: "byMovieId", query: movieID});
But this second approach gives me an error in the console which is:
Response {type: "cors", url: "https://www.omdbapi.com/?apikey=API_KEY&s=harry+potter&type=movie", redirected: true, status: 200, ok: true, …}
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: true
status: 200
statusText: ""
type: "cors"
url: "https://www.omdbapi.com/?apikey=API_KEY&s=harry+potter&type=movie"
The first approach works perfectly but the second one none and trying to get a solution but cannot really think what to do to get this code better optimized.

Since the queries are identical except for the url, create a query generator (createMoviesQuery) that accepts a function that generates the url (urlGenerator), and returns a query function
Example (not tested):
import {API_MOVIE_URL, API_KEY} from "./ApisConst";
const createMoviesQuery = urlGenerator => async (...params) => {
try {
const url = urlGenerator(...params);
const response = await fetch(url);
const json = await response.json();
return json;
} catch {
return {
success: false,
result: [],
message: "There is an issue to get data from server. Please try again later.",
};
}
};
export const getMoviesBySearch = createMoviesQuery((search) => `${API_MOVIE_URL}?apikey=${API_KEY}&${search}`);
export const getMoviesInfo = createMoviesQuery((movieID) => `${API_MOVIE_URL}?apikey=${API_KEY}&i=${movieID}&plot`);

Related

RTK query and response from server

updateTagCurrentValue: builder.mutation<any, {path: string, body: updateTagCurrentValueBody}>({
query: (args) => {
const {path, body} = args
return ({
url: `/v2/tags/${path}/values/current`,
method: 'PUT',
body: body,
})
},
transformResponse: (response) => {
return response
}
})
I am new in RTK, this mutation is working well, but I can not understand how to get a response from server? I see it in inspect network tab, I see my added data in the server, but the response comes null
const [updateTagCurrentValue, {error, isSuccess}] = useUpdateTagCurrentValueMutation()
const onSubmit: SubmitHandler = async (data) => {
let body: updateTagCurrentValueBody = {
value: {value: JSON.stringify(data)}
}
try {
let response = await updateTagCurrentValue({path: 'test', body}).unwrap();
console.log(response)
} catch (err) {
console.log(err)
}
}
I searched for solutions, and some people said add .unwrap() at the end of the call, but it didn't help. transformResponse also changes nothing, isSuccess changes from false to true after the second submission...

Why does the fetch request returns an undefined data in Nextjs?

This is the code in the api sections for my GET request:
export default async (req, res) => {
const { query: { className }, method } = req;
switch (method) {
case "GET":
try {
const classDetail = await Class.findOne({title: className});
if (!classDetail) {
return res.status(400).json({ success: false });
}
res.status(200).json({ success: true, data: classDetail });
} catch (error) {
res.status(400).json({ success: false });
console.log(error);
}
break;
default:
res.status(400).json({ success: false });
}
In my [className] page, I want to fetch the data from http://localhost:3000/api/admin/classes/${className} API. I console logged the response, but it returns undefined for some reason.
export async function getServerSideProps({ query: { className } }) {
const res = await fetch(`http://localhost:3000/api/admin/classes/${className}`)
.then(() => console.log(`first get request sent: `));
// const { data } = await res.json();
console.log(res)
return { props: { classDetail: 1 } };
}
But when I send the same get request from postman using http://localhost:3000/api/admin/classes/class-3 API, it sends me the following data. I'm not getting the data from getServerSideProps.
{
"success": true,
"data": {
"_id": "62f6858ea26fbb47b3cc0563",
"title": "class-3",
"classImageURL": "http://res.cloudinary.com/nhnasem/image/upload/v1660323222/HELP_ME_ewcr5t.png",
"imageWidth": "1555",
"imageHeight": "2000",
"__v": 0
}
}
Why is this happening? And how can I resolve the issue?
Edit: I tried it with jsonplaceholder API. The result is the same undefined.
Edited 2: For two APIs
export async function getServerSideProps({ query: { className } }) {
const res = await fetch(
`http://localhost:3000/api/admin/classes/${className}`
);
const {data} = res.json()
const res2 = await fetch(
`http://localhost:3000/api/admin/classes/${className}/subjects`
);
const {data} = await res2.json() // it won't do, because it was called
// and initialized before
return { props: { classDetail: data, subjects: data} }; // ???????????
}
The reason is that extra .then() that you added to your fetch call.
When you add .then() to a promise chain, it receives the result of fetch as its argument and is supposed to return whatever should be the result of a promise. In your case, you're not returning anything, hence the undefined you get in your res variable. Instead, you should return the original result:
const res = await fetch(`http://localhost:3000/api/admin/classes/${className}`)
.then((res) => { console.log(`first get request sent: `); return res; });
or simply remove
.then(() => console.log(`first get request sent: `));
UPDATE
If both requests return an object with data property, you can't simply destructure both results due to duplicated variable name:
const {data} = await res.json()
const {data} = await res2.json();
What you can do, is to provide the name of a variable where data should be assigned:
const {data: classDetail} = await res.json()
const {data: subjects} = await res2.json();
return { props: { classDetail, subjects} };
or even be more explicit:
const json = await res.json()
const json2 = await res2.json();
return { props: { classDetail: json.data, subjects: json2.data} };

Axios instance/function not receiving argument?

I have a next.js App which has a working axios call, which I am trying to refactor. I have it mostly working, but I can't get my new function to receive arguments.
This problem has two components to it, my next.js page, and the external custom module where I am writing my functions to use axios to call the YouTube API to retrieve info.
My next.js getStaticProps call looks like this. I know this is working. Note the function where I am trying to pass in the video ID. (The 'const = video' line)
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { slug = "" } = context.params;
const film = await client.fetch(query, { slug });
const video = await youtube.grabVideoInfo(film.VideoID);
return {
props: {
film,
video,
},
revalidate: 10,
};
}
I have tried writing the axios call in two ways, trying to pass in the video ID as an argument. Neither of which work, and fail to call from the API, stating an invalid video ID, which means it isn't being passed in.
The first way:
const grabVideoInfo = async (videoId) => {
const videoGrab = axios.create({
baseURL: "https://www.googleapis.com/youtube/v3/videos?",
params: {
headers: { "Access-Control-Allow-Origin": "*" },
part: "snippet",
id: videoId,
key: KEY,
},
});
const query = await videoGrab.get().then(
(response) => {
return response.data.items[0];
},
(error) => {
return error.toJSON();
}
);
return query;
};
The second way:
const grabVideoInfo = async (videoId) => {
const videoGrab = axios.create({
baseURL: "https://www.googleapis.com/youtube/v3/videos?",
params: {
headers: { "Access-Control-Allow-Origin": "*" },
part: "snippet",
key: KEY,
},
});
const query = await videoGrab.get({ params: { id: videoId } }).then(
(response) => {
return response.data.items[0];
},
(error) => {
return error.toJSON();
}
);
return query;
};
And this is the fully working version that I am trying to rewrite, which is live on the app currently. This demonstrates that the getStaticProps client call is working.
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { slug = "" } = context.params;
const film = await client.fetch(query, { slug });
const KEY = process.env.YOUTUBE_API_KEY;
const conn = axios.create({
baseURL: "https://www.googleapis.com/youtube/v3/",
params: {
headers: { "Access-Control-Allow-Origin": "*" },
part: "snippet",
id: film.videoID,
key: KEY,
},
});
const video = await (await conn.get("videos?")).data.items[0];
return {
props: {
film,
video,
},
revalidate: 10,
};
}
Any help is greatly appreciated. I'm really scratching my head with this one.
Ok so your refactor is accessing film.VideoId where the original uses film.videoId.

Getting undefined in API call via Fetch - Express Nuxt

I am extremely stuck on a task using Express, API, Fetch.
I am using Nuxt + Shopify API endpoints to grab data such as orders like below
This is my express API Endpoint.
The results should return an array of objects ( orders )
const bodyParser = require('body-parser')
const app = require('express')()
const axios = require('axios')
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post('/getJSON', async (req, res) => {
const { customerID } = req.body
const id = Buffer.from(customerID, 'base64')
.toString('binary')
.split('gid://shopify/Customer/')
.pop()
console.log('id is', id)
const endpoint = `https://test.myshopify.com/admin/api/2020-07/customers/${id}/orders.json`
try {
const response = await axios.get(endpoint, {
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': '*****************'
}
})
res.status(200).json(response.data.orders)
} catch (error) {
res.status(500).send(error)
}
})
module.exports = app
Now, in my Nuxt store.js, I am using fetch to make a post requests to that endpoint above.
async function apiPost(endpoint, { data }) {
await fetch(`/api${endpoint}/getJSON`, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
}).then(async (res) => {
const contentType = res.headers.get('content-type')
if (contentType.startsWith('text/html')) {
return res.text()
}
if (contentType.startsWith('application/json')) {
await res.json()
}
})
}
When I console.log res.json(), it is a promise
Since it is a promise, I wanted to see what the results were anyways.
res.json().then((resp) => {
console.log('resp is', resp)
})
Turns out the rest is an array of objects.
However, I do not know how to return this object correctly, as the "ordersResponse" below is always undefined !
Below is a function calling the apiPost passing in the 'endpoint'. However, the orderResponse is undefined. I have tried solutions, but all of them end up as orderResponse being undefined.
async fetchOrders({ state, dispatch, commit }, payload) {
try {
const ordersResponse = await apiPost('/customer-orders', {
data: { customerID: state.customer.id }
})
console.log('ordersResponse', ordersResponse) // **undefined**
} catch (error) {
console.error(error)
throw error
}
},
Any help is appreciated
It looks like the apiPost function needs to make a return from within the 'application/json' if block.
if (contentType.startsWith('application/json')) {
return res.json()
}
You should then receive data back when calling it for the ordersResponse variable.

localstorage.getitem('key') sometimes returns null - in a react app

this is a very weird problem! I'm trying to build a login form which sets a JWT token in localstorage. Other forms then use that token to post requests. I can see the token in my console.log just fine, but sometimes (like 3 out of 5 times), when I am setting localstorage.getitem('idToken'), it shows as null. This behavior most noticeably happens when I remove the console.log(idToken) from my loginUser() function (code in actions.js file - given below). What am I doing wrong? my app is built using React/Redux.
action.js
export function loginUser(creds) {
const data = querystring.stringify({_username: creds.username, _password: creds.password});
let config = {
method: 'POST',
headers: { 'Content-Type':'application/x-www-form-urlencoded' },
body: data
};
return dispatch => {
// We dispatch requestLogin to kickoff the call to the API
dispatch(requestLogin(creds));
return fetch(BASE_URL+'login_check', config)
.then(response =>
response.json().then(user => ({ user, response }))
).then(({ user, response }) => {
if (!response.ok) {
// If there was a problem, we want to
// dispatch the error condition
dispatch(loginError(user.message));
return Promise.reject(user)
} else {
localStorage.setItem('idToken', user.token);
let token = localStorage.getItem('idToken')
console.log(token);
// if I remove this log, my token is returned as null during post.
dispatch(receiveLogin(user));
}
}).catch(err => console.log("Error: ", err))
}
}
here's my POST request:
import axios from 'axios';
import {BASE_URL} from './middleware/api';
import {reset} from 'redux-form';
let token = localStorage.getItem('idToken');
const AuthStr = 'Bearer '.concat(token);
let headers ={
headers: { 'Content-Type':'application/json','Authorization' : AuthStr }
};
export default (async function showResults(values, dispatch) {
console.log(AuthStr);
axios.post(BASE_URL + 'human/new', values, headers)
.then(function (response) {
console.log(response);
alert("Your submit was successful");
//dispatch(reset('wizard'));
}).catch(function (error) {
console.log(error.response);
alert(error.response.statusText);
});
});
This GET request works everytime, BTW:
getHouses = (e) => {
let token = localStorage.getItem('idToken') || null;
const AuthStr = 'Bearer '.concat(token);
axios.get(BASE_URL + 'household/list', { headers: { Authorization: AuthStr } }).then((response) =>
{
let myData = response.data;
let list = [];
let key =[];
for (let i = 0; i < myData._embedded.length; i++) {
let embedded = myData._embedded[i];
list.push(embedded.friendlyName);
key.push(embedded.id);
}
this.setState({data: list, key: key});
})
.catch((error) => {
console.log('error' + error);
});
}
I'm at my wit's end! Please help!
The localStorage.setItem() is a asynchronous task, and sometimes you run let token = localStorage.getItem('idToken') just after the setItem will fail, so you get a null, so please put the getItem operation some later, have a try, it will be different :
setTimeout(function() {
let token = localStorage.getItem('idToken');
dispatch(receiveLogin(user));
}, 50);
Move your token logic (i.e. localStorage.getItem('idToken');) inside the exported function and it should work
export default (async function showResults(values, dispatch) {
let token = localStorage.getItem('idToken');
const AuthStr = 'Bearer '.concat(token);
let headers ={
headers: { 'Content-Type':'application/json','Authorization' : AuthStr
}
};
axios.post(BASE_URL + 'human/new', values, headers)...
There can't be a case where you set a key value in localstorage and then it returns you null, immediately in the next line.
localStorage.setItem('idToken', user.token);
let token = localStorage.getItem('idToken');
This will only happen if your user.token value is null.
Maybe the case here is your thennable function not returning value to your next then like this:
....
.then(response =>
// return response to your next then function
// this will be passed to next then function as params
return response.json();
).then(({ user, response }) => {
....
Make a function whose return the value or a default value
const [hideTyC, setHideTyC] = useState(false);
const loadTyCFlag = (): any => {
if (
localStorage.getItem("tyc") !== null ||
localStorage.getItem("tyc") !== undefined
) {
return localStorage.getItem("tyc") || false;
}
};
useIonViewDidEnter(() => {
hideTabBar();
setHideTyC(loadTyCFlag());
});

Categories

Resources