A property named "locale" in my API response body is being overridden in some browsers - javascript

I have an API returning this kind of payload in its response body:
{ "id": 1, "name": [{ "locale": "en", "value": "example" }] }
I'm calling this API using the following code in a React web app:
async function getAsync(url) {
return fetch(url, {
method: "GET",
mode: "cors",
credentials: "include",
headers: {
...commonHeaders, // Just for auth
},
})
}
async function getResponse() {
const url = `...` // API url
let res
try {
res = await getAsync(url)
switch (res.status) {
case 200:
const resBody = await res.json()
return Promise.resolve(resBody)
...
default:
return Promise.reject(new GenericError())
}
} catch (err) {
return Promise.reject(new GenericError())
}
}
The problem is the following: When I print in console JSON.stringify(resBody) I get the correct payload (of course as a JSON string), but if I print resBody its property name.locale has my locale ("it") instead of "en"!
Why is it happening? What's going on here???
This happens in both Firefox (85.0.2) and Chrome (88.0.4324.150) but NOT in Safari (14.0.2).

I've found the reason! It was a nasty side-effect present in a completely different asynchronous function in my UI, caused by these very wrong comparisons (please notice = used instead of ===):
{
name_en: name.filter(item => item.locale = "en").value || "",
name_it: name.filter(item => item.locale = "it").value || "",
}

Related

How can I resolve the error of 'serializing' from getServerSideProps in Next.js

I just changed the server from const server = "https://backend.yourguide.pk" to const server = "https://stage.yourguide.pk", and now this error is shown. How to resolve it?
getServerSideProps's code:
export async function getServerSideProps(context) {
let experience = [];
const server = 'stage.yourguide.pk';
try {
const data = await fetch(
`${server}/api/user/getallexperience?title=${context.params.experiencesTitle}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}
);
experience = await data.json();
} catch (error) {
console.log('error is ', error);
}
return {
props: {
title: context.params.experiencesTitle,
experience: experience.results,
},
};
}
error - SerializableError: Error serializing .experiences returned from getServerSideProps in "/".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
For some reason, experience is being set to undefined, and undefined is not a valid JSON value. This can be addressed with the help of a check, like so:
export async function getServerSideProps(context) {
let experience;
const server = "stage.yourguide.pk";
try {
const data = await fetch(
`${server}/api/user/getallexperience?title=${context.params?.experiencesTitle}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
}
);
experience = await data.json();
} catch (error) {
console.log("error is ", error);
}
return {
props: {
title: context.params?.experiencesTitle || null,
experience: experience?.results || null,
},
};
}
Now, why you are getting undefined? Well, this can be because you are not using the correct endpoint, or path, or not giving the correct params, etc. Also, your error is saying .experiences while you have .experience.
Make sure you are not making any typos.

JavaScript skip await on pending fetch [duplicate]

This question already has answers here:
Fetch API request timeout?
(14 answers)
Closed 8 months ago.
I have an API fetch await code that fetches list of nodes in an array. The problem is, some of the nodes don't respond for whatever reason (being offline, wrong port, being programmed NOT to respond, ... ) and my code is stuck awaiting a reply from that node.
Is there any way to stop awaiting a fetch for example after 3 seconds if no response comes?
I tried using try and catch, but the nodes that don't respond don't return anything, the code is simply sitting there with no error or response.
Thank you!!
// list of nodes
let nodes = [{
"address": {
"hostname": "192.168.1.1",
"port": 31350
}
}, {
"address": {
"hostname": "192.168.1.2",
"port": 31350
}
}
]
// api fetch function
async function fetchNodes(hostname, port) {
const response = await fetch(`https://${hostname}:${port}/getstatus`, {
method: 'post',
body: JSON.stringify(body),
headers: {'Content-Type': 'application/json'}
});
const data = response.json();
console.log(data);
}
// loop to call api fetch function with all array entries
nodes.forEach(function(entry) {
fetchNodes(entry.address.hostname, entry.address.port);
}
)
try this
async function fetchWithTimeout(resource, options = {}) {
const { timeout = 8000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal
});
clearTimeout(id);
return response;
}
and use this function in you fetchNodes function
async function fetchNodes() {
try {
const response = await fetchWithTimeout(
`https://${hostname}:${port}/getstatus`,
{
timeout: 6000,
method: "post",
body: JSON.stringify(body),
headers: { "Content-Type": "application/json" },
}
);
const data = await response.json();
return data;
} catch (error) {
// Timeouts if the request takes
// longer than 6 seconds
console.log(error.name === "AbortError");
}
}

Rejected promise with SyntaxError: Unexpected token i in JSON at position 0 when fetching raw file from Gitlab

I'm trying to fetch a raw file from Gitlab repository, following official documentation.
The functions are the following:
methods: {
async getProjects(url, method, payload) {
const token = this.$store.getters.token
const headers = {
'Content-Type': 'application/json',
'Private-Token': token
}
const response = await fetch(url, {
method: method,
headers: headers,
body: JSON.stringify(payload)
})
return response.json()
},
[...]
async addNewProject() {
const payload = {
"name": "newProjectName",
"namespace_id": 12,
"description": "description"
}
this.getProjects("https://gitlab.example.com/api/v4/projects/", "POST", payload)
.then(data => {
console.log(data.id)
})
.catch((e) => {
console.error(e)
})
let rawFile = null
try {
rawFile = await JSON.parse(this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET"))
} catch (e) {
rawFile = this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET")
}
console.log(rawFile)
}
}
Logging the rawFile shows a pending Promise object with rejected state and the SyntaxError as in the title.
Is it possible that the raw format of the file is causing this error? If so, how to prevent it?
There are a few things going on.
gitlab.example.com doesn't exist, so it's difficult to know what results you actually get
You are passing the promise to JSON.parse. You need to use await or then to use the response:
The end of your getProjects calls response.json() which will parse the json. You don't need to parse it again.
How about using:
let rawFile = await this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET")
If the response is json, that would give you an object, but if the response is not json, you want to use response.text() instead. You'd need a different function:
async getTextFile(url) {
const token = this.$store.getters.token
const headers = {
'Private-Token': token
}
const response = await fetch(url, {
method: "GET",
headers: headers
})
return response.text()
}
then call it like this:
let rawFile = await this.getTextFile("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master")

Updated object not being returned properly in nextjs

So basically I'm working on a nextjs app which uses authentication. I have a 2 functions which I run on every page load. The first checks if jwt cookies exist and calls another function to validate the tokens if they don't exist. This function is ran from wrapper.getServerSideProps and is passed in the context as ctx. This function works as intended.
export const checkServerSideCookie = (ctx) => {
const access = getCookie("access", ctx.req);
const refresh = getCookie("refresh", ctx.req);
if (access && refresh) {
return checkAuthentication(access, refresh);
} else return { isAuthenticated: false, token: null };
};
The second function is the token validator and this is where the issue arises. I have an object which I intended to update if the validation is successful and leave alone if it isn't. Here is the function
export const checkAuthentication = (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null,
};
const body = JSON.stringify({ token: access });
axios
.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
obj.isAuthenticated = true;
obj.token = access;
})
.catch((err) => {
// call new token function using refresh
console.log("it doesnt work");
});
return obj;
};
The issue is is that the .then does update the object, and when I console.log(obj) in the .then it shows the proper obj to return, however when I return the obj it still holds the initial values of false and null. I don't understand what the issue is. I try doing the return in the .then itself but it throughs this error
TypeError: Cannot destructure property 'isAuthenticated' of 'Object(...)(...)' as it is undefined.
What is the issue here? It all seems good but the updated obj isn't returned.
axios.post is async, you're returning the obj before it gets filled with data from the api response, you can use async/await to solve that :
export const checkAuthentication = async (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null
};
const body = JSON.stringify({ token: access });
try {
const res = await axios.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json"
}
});
obj.isAuthenticated = true;
obj.token = access;
} catch (e) {
// do something with the error
// call new token function using refresh
console.log("it doesnt work");
}
return obj;
};
usage (checkAuthentication now return a promise ) :
checkAuthentication(a, b).then((obj) => {
console.log(obj);
});
When you call checkAuthentication it immediately returns the obj with the default properties. You have an asynchronous operation specified in your function, however you don't wait until it's done. You'd have to rebuild your function the following way:
export const checkAuthentication = (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null,
};
const body = JSON.stringify({ token: access });
return new Promise((resolve, reject) => {
axios
.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
resolve({
isAuthenticated: true,
token: access
})
})
.catch((err) => {
// call new token function using refresh
console.log("it doesnt work");
reject();
});
});
};
and then call your function the following way:
checkAuthentication(access, refresh)
.then(console.log)
.catch(console.log)
You, of course, have multiple options to make your function cleaner, such as by using async/await etc, but this should give you a quick overview of what is wrong.

How to optimize an API fetch to be less redundant

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`);

Categories

Resources